Merge remote-tracking branch 'upstream/develop' into bzz

This commit is contained in:
zelig 2015-04-02 04:52:00 +01:00
commit 77d7ef927c
107 changed files with 6412 additions and 1852 deletions

6
.gitignore vendored
View file

@ -13,6 +13,8 @@
.ethtest .ethtest
*/**/*tx_database* */**/*tx_database*
*/**/*dapps* */**/*dapps*
Godeps/_workspace/pkg
Godeps/_workspace/bin
#* #*
.#* .#*
@ -21,7 +23,9 @@
.project .project
.settings .settings
cmd/ethereum/ethereum geth
mist
cmd/geth/geth
cmd/mist/mist cmd/mist/mist
deploy/osx/Mist.app deploy/osx/Mist.app
deploy/osx/Mist\ Installer.dmg deploy/osx/Mist\ Installer.dmg

3
.gitmodules vendored
View file

@ -1,6 +1,3 @@
[submodule "ethereal/assets/samplecoin"]
path = ethereal/assets/samplecoin
url = git@github.com:obscuren/SampleCoin.git
[submodule "cmd/mist/assets/ext/ethereum.js"] [submodule "cmd/mist/assets/ext/ethereum.js"]
path = cmd/mist/assets/ext/ethereum.js path = cmd/mist/assets/ext/ethereum.js
url = https://github.com/ethereum/ethereum.js url = https://github.com/ethereum/ethereum.js

View file

@ -10,3 +10,5 @@ Joseph Goulden <joegoulden@gmail.com>
Nick Savers <nicksavers@gmail.com> Nick Savers <nicksavers@gmail.com>
Maran Hidskes <maran.hidskes@gmail.com> Maran Hidskes <maran.hidskes@gmail.com>
Taylor Gerring <taylor.gerring@gmail.com> <taylor.gerring@ethereum.org>

View file

@ -9,7 +9,7 @@ install:
# - go get code.google.com/p/go.tools/cmd/goimports # - go get code.google.com/p/go.tools/cmd/goimports
# - go get github.com/golang/lint/golint # - go get github.com/golang/lint/golint
# - go get golang.org/x/tools/cmd/vet # - go get golang.org/x/tools/cmd/vet
- if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi - go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls - go get github.com/mattn/goveralls
before_script: before_script:
# - gofmt -l -w . # - gofmt -l -w .

View file

@ -30,10 +30,10 @@ RUN mkdir -p $GOPATH/src/github.com/ethereum/
RUN git clone https://github.com/ethereum/go-ethereum $GOPATH/src/github.com/ethereum/go-ethereum RUN git clone https://github.com/ethereum/go-ethereum $GOPATH/src/github.com/ethereum/go-ethereum
WORKDIR $GOPATH/src/github.com/ethereum/go-ethereum WORKDIR $GOPATH/src/github.com/ethereum/go-ethereum
RUN git checkout develop RUN git checkout develop
RUN GOPATH=$GOPATH:$GOPATH/src/github.com/ethereum/go-ethereum/Godeps/_workspace go install -v ./cmd/ethereum RUN GOPATH=$GOPATH:$GOPATH/src/github.com/ethereum/go-ethereum/Godeps/_workspace go install -v ./cmd/geth
## Run & expose JSON RPC ## Run & expose JSON RPC
ENTRYPOINT ["ethereum", "-rpc=true", "-rpcport=8545"] ENTRYPOINT ["geth", "-rpc=true", "-rpcport=8545"]
EXPOSE 8545 EXPOSE 8545

4
Godeps/Godeps.json generated
View file

@ -90,6 +90,10 @@
"ImportPath": "github.com/robertkrimen/otto/token", "ImportPath": "github.com/robertkrimen/otto/token",
"Rev": "dea31a3d392779af358ec41f77a07fcc7e9d04ba" "Rev": "dea31a3d392779af358ec41f77a07fcc7e9d04ba"
}, },
{
"ImportPath": "github.com/rs/cors",
"Rev": "6e0c3cb65fc0fdb064c743d176a620e3ca446dfb"
},
{ {
"ImportPath": "github.com/syndtr/goleveldb/leveldb", "ImportPath": "github.com/syndtr/goleveldb/leveldb",
"Rev": "832fa7ed4d28545eab80f19e1831fc004305cade" "Rev": "832fa7ed4d28545eab80f19e1831fc004305cade"

4
Godeps/_workspace/src/github.com/rs/cors/.travis.yml generated vendored Normal file
View file

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

19
Godeps/_workspace/src/github.com/rs/cors/LICENSE generated vendored Normal file
View file

@ -0,0 +1,19 @@
Copyright (c) 2014 Olivier Poitrey <rs@dailymotion.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

84
Godeps/_workspace/src/github.com/rs/cors/README.md generated vendored Normal file
View file

@ -0,0 +1,84 @@
# 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).

37
Godeps/_workspace/src/github.com/rs/cors/bench_test.go generated vendored Normal file
View file

@ -0,0 +1,37 @@
package cors
import (
"net/http"
"net/http/httptest"
"testing"
)
func BenchmarkWithout(b *testing.B) {
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
for i := 0; i < b.N; i++ {
testHandler.ServeHTTP(res, req)
}
}
func BenchmarkDefault(b *testing.B) {
res := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "http://example.com/foo", nil)
handler := Default()
for i := 0; i < b.N; i++ {
handler.Handler(testHandler).ServeHTTP(res, req)
}
}
func BenchmarkPreflight(b *testing.B) {
res := httptest.NewRecorder()
req, _ := http.NewRequest("OPTIONS", "http://example.com/foo", nil)
req.Header.Add("Access-Control-Request-Method", "GET")
handler := Default()
for i := 0; i < b.N; i++ {
handler.Handler(testHandler).ServeHTTP(res, req)
}
}

308
Godeps/_workspace/src/github.com/rs/cors/cors.go generated vendored Normal file
View file

@ -0,0 +1,308 @@
/*
Package cors is net/http handler to handle CORS related requests
as defined by http://www.w3.org/TR/cors/
You can configure it by passing an option struct to cors.New:
c := cors.New(cors.Options{
AllowedOrigins: []string{"foo.com"},
AllowedMethods: []string{"GET", "POST", "DELETE"},
AllowCredentials: true,
})
Then insert the handler in the chain:
handler = c.Handler(handler)
See Options documentation for more options.
The resulting handler is a standard net/http handler.
*/
package cors
import (
"log"
"net/http"
"os"
"strconv"
"strings"
)
// Options is a configuration container to setup the CORS middleware.
type Options struct {
// AllowedOrigins is a list of origins a cross-domain request can be executed from.
// If the special "*" value is present in the list, all origins will be allowed.
// Default value is ["*"]
AllowedOrigins []string
// AllowedMethods is a list of methods the client is allowed to use with
// cross-domain requests. Default value is simple methods (GET and POST)
AllowedMethods []string
// AllowedHeaders is list of non simple headers the client is allowed to use with
// cross-domain requests.
// If the special "*" value is present in the list, all headers will be allowed.
// Default value is [] but "Origin" is always appended to the list.
AllowedHeaders []string
// ExposedHeaders indicates which headers are safe to expose to the API of a CORS
// API specification
ExposedHeaders []string
// AllowCredentials indicates whether the request can include user credentials like
// cookies, HTTP authentication or client side SSL certificates.
AllowCredentials bool
// MaxAge indicates how long (in seconds) the results of a preflight request
// can be cached
MaxAge int
// Debugging flag adds additional output to debug server side CORS issues
Debug bool
// log object to use when debugging
log *log.Logger
}
type Cors struct {
// The CORS Options
options Options
}
// New creates a new Cors handler with the provided options.
func New(options Options) *Cors {
// Normalize options
// Note: for origins and methods matching, the spec requires a case-sensitive matching.
// As it may error prone, we chose to ignore the spec here.
normOptions := Options{
AllowedOrigins: convert(options.AllowedOrigins, strings.ToLower),
AllowedMethods: convert(options.AllowedMethods, strings.ToUpper),
// Origin is always appended as some browsers will always request
// for this header at preflight
AllowedHeaders: convert(append(options.AllowedHeaders, "Origin"), http.CanonicalHeaderKey),
ExposedHeaders: convert(options.ExposedHeaders, http.CanonicalHeaderKey),
AllowCredentials: options.AllowCredentials,
MaxAge: options.MaxAge,
Debug: options.Debug,
log: log.New(os.Stdout, "[cors] ", log.LstdFlags),
}
if len(normOptions.AllowedOrigins) == 0 {
// Default is all origins
normOptions.AllowedOrigins = []string{"*"}
}
if len(normOptions.AllowedHeaders) == 1 {
// Add some sensible defaults
normOptions.AllowedHeaders = []string{"Origin", "Accept", "Content-Type"}
}
if len(normOptions.AllowedMethods) == 0 {
// Default is simple methods
normOptions.AllowedMethods = []string{"GET", "POST"}
}
if normOptions.Debug {
normOptions.log.Printf("Options: %v", normOptions)
}
return &Cors{
options: normOptions,
}
}
// Default creates a new Cors handler with default options
func Default() *Cors {
return New(Options{})
}
// Handler apply the CORS specification on the request, and add relevant CORS headers
// as necessary.
func (cors *Cors) Handler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "OPTIONS" {
cors.logf("Handler: Preflight request")
cors.handlePreflight(w, r)
// Preflight requests are standalone and should stop the chain as some other
// middleware may not handle OPTIONS requests correctly. One typical example
// is authentication middleware ; OPTIONS requests won't carry authentication
// headers (see #1)
} else {
cors.logf("Handler: Actual request")
cors.handleActualRequest(w, r)
h.ServeHTTP(w, r)
}
})
}
// Martini compatible handler
func (cors *Cors) HandlerFunc(w http.ResponseWriter, r *http.Request) {
if r.Method == "OPTIONS" {
cors.logf("HandlerFunc: Preflight request")
cors.handlePreflight(w, r)
} else {
cors.logf("HandlerFunc: Actual request")
cors.handleActualRequest(w, r)
}
}
// Negroni compatible interface
func (cors *Cors) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
if r.Method == "OPTIONS" {
cors.logf("ServeHTTP: Preflight request")
cors.handlePreflight(w, r)
// Preflight requests are standalone and should stop the chain as some other
// middleware may not handle OPTIONS requests correctly. One typical example
// is authentication middleware ; OPTIONS requests won't carry authentication
// headers (see #1)
} else {
cors.logf("ServeHTTP: Actual request")
cors.handleActualRequest(w, r)
next(w, r)
}
}
// handlePreflight handles pre-flight CORS requests
func (cors *Cors) handlePreflight(w http.ResponseWriter, r *http.Request) {
options := cors.options
headers := w.Header()
origin := r.Header.Get("Origin")
if r.Method != "OPTIONS" {
cors.logf(" Preflight aborted: %s!=OPTIONS", r.Method)
return
}
if origin == "" {
cors.logf(" Preflight aborted: empty origin")
return
}
if !cors.isOriginAllowed(origin) {
cors.logf(" Preflight aborted: origin '%s' not allowed", origin)
return
}
reqMethod := r.Header.Get("Access-Control-Request-Method")
if !cors.isMethodAllowed(reqMethod) {
cors.logf(" Preflight aborted: method '%s' not allowed", reqMethod)
return
}
reqHeaders := parseHeaderList(r.Header.Get("Access-Control-Request-Headers"))
if !cors.areHeadersAllowed(reqHeaders) {
cors.logf(" Preflight aborted: headers '%v' not allowed", reqHeaders)
return
}
headers.Set("Access-Control-Allow-Origin", origin)
headers.Add("Vary", "Origin")
// Spec says: Since the list of methods can be unbounded, simply returning the method indicated
// by Access-Control-Request-Method (if supported) can be enough
headers.Set("Access-Control-Allow-Methods", strings.ToUpper(reqMethod))
if len(reqHeaders) > 0 {
// Spec says: Since the list of headers can be unbounded, simply returning supported headers
// from Access-Control-Request-Headers can be enough
headers.Set("Access-Control-Allow-Headers", strings.Join(reqHeaders, ", "))
}
if options.AllowCredentials {
headers.Set("Access-Control-Allow-Credentials", "true")
}
if options.MaxAge > 0 {
headers.Set("Access-Control-Max-Age", strconv.Itoa(options.MaxAge))
}
cors.logf(" Preflight response headers: %v", headers)
}
// handleActualRequest handles simple cross-origin requests, actual request or redirects
func (cors *Cors) handleActualRequest(w http.ResponseWriter, r *http.Request) {
options := cors.options
headers := w.Header()
origin := r.Header.Get("Origin")
if r.Method == "OPTIONS" {
cors.logf(" Actual request no headers added: method == %s", r.Method)
return
}
if origin == "" {
cors.logf(" Actual request no headers added: missing origin")
return
}
if !cors.isOriginAllowed(origin) {
cors.logf(" Actual request no headers added: origin '%s' not allowed", origin)
return
}
// Note that spec does define a way to specifically disallow a simple method like GET or
// POST. Access-Control-Allow-Methods is only used for pre-flight requests and the
// spec doesn't instruct to check the allowed methods for simple cross-origin requests.
// We think it's a nice feature to be able to have control on those methods though.
if !cors.isMethodAllowed(r.Method) {
if cors.options.Debug {
cors.logf(" Actual request no headers added: method '%s' not allowed",
r.Method)
}
return
}
headers.Set("Access-Control-Allow-Origin", origin)
headers.Add("Vary", "Origin")
if len(options.ExposedHeaders) > 0 {
headers.Set("Access-Control-Expose-Headers", strings.Join(options.ExposedHeaders, ", "))
}
if options.AllowCredentials {
headers.Set("Access-Control-Allow-Credentials", "true")
}
cors.logf(" Actual response added headers: %v", headers)
}
// convenience method. checks if debugging is turned on before printing
func (cors *Cors) logf(format string, a ...interface{}) {
if cors.options.Debug {
cors.options.log.Printf(format, a...)
}
}
// isOriginAllowed checks if a given origin is allowed to perform cross-domain requests
// on the endpoint
func (cors *Cors) isOriginAllowed(origin string) bool {
allowedOrigins := cors.options.AllowedOrigins
origin = strings.ToLower(origin)
for _, allowedOrigin := range allowedOrigins {
switch allowedOrigin {
case "*":
return true
case origin:
return true
}
}
return false
}
// isMethodAllowed checks if a given method can be used as part of a cross-domain request
// on the endpoing
func (cors *Cors) isMethodAllowed(method string) bool {
allowedMethods := cors.options.AllowedMethods
if len(allowedMethods) == 0 {
// If no method allowed, always return false, even for preflight request
return false
}
method = strings.ToUpper(method)
if method == "OPTIONS" {
// Always allow preflight requests
return true
}
for _, allowedMethod := range allowedMethods {
if allowedMethod == method {
return true
}
}
return false
}
// areHeadersAllowed checks if a given list of headers are allowed to used within
// a cross-domain request.
func (cors *Cors) areHeadersAllowed(requestedHeaders []string) bool {
if len(requestedHeaders) == 0 {
return true
}
for _, header := range requestedHeaders {
found := false
for _, allowedHeader := range cors.options.AllowedHeaders {
if allowedHeader == "*" || allowedHeader == header {
found = true
break
}
}
if !found {
return false
}
}
return true
}

288
Godeps/_workspace/src/github.com/rs/cors/cors_test.go generated vendored Normal file
View file

@ -0,0 +1,288 @@
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

@ -0,0 +1,24 @@
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

@ -0,0 +1,18 @@
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

@ -0,0 +1,22 @@
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

@ -0,0 +1,23 @@
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

@ -0,0 +1,26 @@
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

@ -0,0 +1,20 @@
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

@ -0,0 +1,22 @@
package main
import (
"net/http"
"github.com/rs/cors"
)
func main() {
c := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE"},
AllowCredentials: true,
})
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte("{\"hello\": \"world\"}"))
})
http.ListenAndServe(":8080", c.Handler(h))
}

27
Godeps/_workspace/src/github.com/rs/cors/utils.go generated vendored Normal file
View file

@ -0,0 +1,27 @@
package cors
import (
"net/http"
"strings"
)
type converter func(string) string
// convert converts a list of string using the passed converter function
func convert(s []string, c converter) []string {
out := []string{}
for _, i := range s {
out = append(out, c(i))
}
return out
}
func parseHeaderList(headerList string) (headers []string) {
for _, header := range strings.Split(headerList, ",") {
header = http.CanonicalHeaderKey(strings.TrimSpace(header))
if header != "" {
headers = append(headers, header)
}
}
return headers
}

28
Godeps/_workspace/src/github.com/rs/cors/utils_test.go generated vendored Normal file
View file

@ -0,0 +1,28 @@
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

@ -4,8 +4,8 @@ Ethereum Go Client © 2014 Jeffrey Wilcke.
| Linux | OSX | Windows | Tests | Linux | OSX | Windows | Tests
----------|---------|-----|---------|------ ----------|---------|-----|---------|------
develop | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20develop%20branch)](https://build.ethdev.com/builders/Linux%20Go%20develop%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20develop%20branch)](https://build.ethdev.com/builders/OSX%20Go%20develop%20branch/builds/-1) | N/A | [![Buildr+Status](https://travis-ci.org/ethereum/go-ethereum.svg?branch=develop)](https://travis-ci.org/ethereum/go-ethereum) develop | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20develop%20branch)](https://build.ethdev.com/builders/Linux%20Go%20develop%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20develop%20branch)](https://build.ethdev.com/builders/OSX%20Go%20develop%20branch/builds/-1) | N/A | [![Buildr+Status](https://travis-ci.org/ethereum/go-ethereum.svg?branch=develop)](https://travis-ci.org/ethereum/go-ethereum) [![Coverage Status](https://coveralls.io/repos/ethereum/go-ethereum/badge.svg?branch=develop)](https://coveralls.io/r/ethereum/go-ethereum?branch=develop)
master | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20master%20branch)](https://build.ethdev.com/builders/Linux%20Go%20master%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=OSX%20Go%20master%20branch)](https://build.ethdev.com/builders/OSX%20Go%20master%20branch/builds/-1) | N/A | [![Buildr+Status](https://travis-ci.org/ethereum/go-ethereum.svg?branch=master)](https://travis-ci.org/ethereum/go-ethereum) master | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=Linux%20Go%20master%20branch)](https://build.ethdev.com/builders/Linux%20Go%20master%20branch/builds/-1) | [![Build+Status](https://build.ethdev.com/buildstatusimage?builder=OSX%20Go%20master%20branch)](https://build.ethdev.com/builders/OSX%20Go%20master%20branch/builds/-1) | N/A | [![Buildr+Status](https://travis-ci.org/ethereum/go-ethereum.svg?branch=master)](https://travis-ci.org/ethereum/go-ethereum) [![Coverage Status](https://coveralls.io/repos/ethereum/go-ethereum/badge.svg?branch=master)](https://coveralls.io/r/ethereum/go-ethereum?branch=master)
[![Bugs](https://badge.waffle.io/ethereum/go-ethereum.png?label=bug&title=Bugs)](https://waffle.io/ethereum/go-ethereum) [![Bugs](https://badge.waffle.io/ethereum/go-ethereum.png?label=bug&title=Bugs)](https://waffle.io/ethereum/go-ethereum)
[![Stories in Ready](https://badge.waffle.io/ethereum/go-ethereum.png?label=ready&title=Ready)](https://waffle.io/ethereum/go-ethereum) [![Stories in Ready](https://badge.waffle.io/ethereum/go-ethereum.png?label=ready&title=Ready)](https://waffle.io/ethereum/go-ethereum)
@ -20,9 +20,9 @@ Mist (GUI):
`go get github.com/ethereum/go-ethereum/cmd/mist` `go get github.com/ethereum/go-ethereum/cmd/mist`
Ethereum (CLI): Geth (CLI):
`go get github.com/ethereum/go-ethereum/cmd/ethereum` `go get github.com/ethereum/go-ethereum/cmd/geth`
As of POC-8, go-ethereum uses [Godep](https://github.com/tools/godep) to manage dependencies. Assuming you have [your environment all set up](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum), switch to the go-ethereum repository root folder, and build/install the executable you need: As of POC-8, go-ethereum uses [Godep](https://github.com/tools/godep) to manage dependencies. Assuming you have [your environment all set up](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum), switch to the go-ethereum repository root folder, and build/install the executable you need:
@ -32,10 +32,10 @@ Mist (GUI):
godep go build -v ./cmd/mist godep go build -v ./cmd/mist
``` ```
Ethereum (CLI): Geth (CLI):
``` ```
godep go build -v ./cmd/ethereum godep go build -v ./cmd/geth
``` ```
Instead of `build`, you can use `install` which will also install the resulting binary. Instead of `build`, you can use `install` which will also install the resulting binary.
@ -61,7 +61,7 @@ Go Ethereum comes with several wrappers/executables found in
[the `cmd` directory](https://github.com/ethereum/go-ethereum/tree/develop/cmd): [the `cmd` directory](https://github.com/ethereum/go-ethereum/tree/develop/cmd):
* `mist` Official Ethereum Browser (ethereum GUI client) * `mist` Official Ethereum Browser (ethereum GUI client)
* `ethereum` Ethereum CLI (ethereum command line interface client) * `geth` Ethereum CLI (ethereum command line interface client)
* `bootnode` runs a bootstrap node for the Discovery Protocol * `bootnode` runs a bootstrap node for the Discovery Protocol
* `ethtest` test tool which runs with the [tests](https://github.com/ethereum/testes) suite: * `ethtest` test tool which runs with the [tests](https://github.com/ethereum/testes) suite:
`cat file | ethtest`. `cat file | ethtest`.
@ -73,12 +73,12 @@ Go Ethereum comes with several wrappers/executables found in
Command line options Command line options
============================ ============================
Both `mist` and `ethereum` can be configured via command line options, environment variables and config files. Both `mist` and `geth` can be configured via command line options, environment variables and config files.
To get the options available: To get the options available:
``` ```
ethereum -help geth -help
``` ```
For further details on options, see the [wiki](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options) For further details on options, see the [wiki](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options)

View file

@ -36,9 +36,8 @@ import (
"bytes" "bytes"
"crypto/ecdsa" "crypto/ecdsa"
crand "crypto/rand" crand "crypto/rand"
"os"
"errors" "errors"
"os"
"sync" "sync"
"time" "time"
@ -82,13 +81,7 @@ func (am *Manager) HasAccount(addr []byte) bool {
return false return false
} }
// Coinbase returns the account address that mining rewards are sent to. func (am *Manager) Primary() (addr []byte, err error) {
func (am *Manager) Coinbase() (addr []byte, err error) {
// TODO: persist coinbase address on disk
return am.firstAddr()
}
func (am *Manager) firstAddr() ([]byte, error) {
addrs, err := am.keyStore.GetKeyAddresses() addrs, err := am.keyStore.GetKeyAddresses()
if os.IsNotExist(err) { if os.IsNotExist(err) {
return nil, ErrNoKeys return nil, ErrNoKeys
@ -208,3 +201,37 @@ func zeroKey(k *ecdsa.PrivateKey) {
b[i] = 0 b[i] = 0
} }
} }
// USE WITH CAUTION = this will save an unencrypted private key on disk
// no cli or js interface
func (am *Manager) Export(path string, addr []byte, keyAuth string) error {
key, err := am.keyStore.GetKey(addr, keyAuth)
if err != nil {
return err
}
return crypto.SaveECDSA(path, key.PrivateKey)
}
func (am *Manager) Import(path string, keyAuth string) (Account, error) {
privateKeyECDSA, err := crypto.LoadECDSA(path)
if err != nil {
return Account{}, err
}
key := crypto.NewKeyFromECDSA(privateKeyECDSA)
if err = am.keyStore.StoreKey(key, keyAuth); err != nil {
return Account{}, err
}
return Account{Address: key.Address}, nil
}
func (am *Manager) ImportPreSaleKey(keyJSON []byte, password string) (acc Account, err error) {
var key *crypto.Key
key, err = crypto.ImportPreSaleKey(am.keyStore, keyJSON, password)
if err != nil {
return
}
if err = am.keyStore.StoreKey(key, password); err != nil {
return
}
return Account{Address: key.Address}, nil
}

View file

@ -1,7 +1,7 @@
package blockpool package blockpool
import ( import (
// "fmt" "fmt"
"testing" "testing"
"time" "time"
@ -45,17 +45,15 @@ func getStatusValues(s *Status) []int {
func checkStatus(t *testing.T, bp *BlockPool, syncing bool, expected []int) (err error) { func checkStatus(t *testing.T, bp *BlockPool, syncing bool, expected []int) (err error) {
s := bp.Status() s := bp.Status()
if s.Syncing != syncing { if s.Syncing != syncing {
t.Errorf("status for Syncing incorrect. expected %v, got %v", syncing, s.Syncing) err = fmt.Errorf("status for Syncing incorrect. expected %v, got %v", syncing, s.Syncing)
return
} }
got := getStatusValues(s) got := getStatusValues(s)
for i, v := range expected { for i, v := range expected {
if i == 0 || i == 7 {
continue //hack
}
err = test.CheckInt(statusFields[i], got[i], v, t) err = test.CheckInt(statusFields[i], got[i], v, t)
// fmt.Printf("%v: %v (%v)\n", statusFields[i], got[i], v) // fmt.Printf("%v: %v (%v)\n", statusFields[i], got[i], v)
if err != nil { if err != nil {
return err return
} }
} }
return return
@ -63,6 +61,25 @@ func checkStatus(t *testing.T, bp *BlockPool, syncing bool, expected []int) (err
func TestBlockPoolStatus(t *testing.T) { func TestBlockPoolStatus(t *testing.T) {
test.LogInit() test.LogInit()
var err error
n := 3
for n > 0 {
n--
err = testBlockPoolStatus(t)
if err != nil {
t.Log(err)
continue
} else {
return
}
}
if err != nil {
t.Errorf("no pass out of 3: %v", err)
}
}
func testBlockPoolStatus(t *testing.T) (err error) {
_, blockPool, blockPoolTester := newTestBlockPool(t) _, blockPool, blockPoolTester := newTestBlockPool(t)
blockPoolTester.blockChain[0] = nil blockPoolTester.blockChain[0] = nil
blockPoolTester.initRefBlockChain(12) blockPoolTester.initRefBlockChain(12)
@ -70,6 +87,7 @@ func TestBlockPoolStatus(t *testing.T) {
delete(blockPoolTester.refBlockChain, 6) delete(blockPoolTester.refBlockChain, 6)
blockPool.Start() blockPool.Start()
defer blockPool.Stop()
blockPoolTester.tds = make(map[int]int) blockPoolTester.tds = make(map[int]int)
blockPoolTester.tds[9] = 1 blockPoolTester.tds[9] = 1
blockPoolTester.tds[11] = 3 blockPoolTester.tds[11] = 3
@ -79,73 +97,67 @@ func TestBlockPoolStatus(t *testing.T) {
peer2 := blockPoolTester.newPeer("peer2", 2, 6) peer2 := blockPoolTester.newPeer("peer2", 2, 6)
peer3 := blockPoolTester.newPeer("peer3", 3, 11) peer3 := blockPoolTester.newPeer("peer3", 3, 11)
peer4 := blockPoolTester.newPeer("peer4", 1, 9) peer4 := blockPoolTester.newPeer("peer4", 1, 9)
// peer1 := blockPoolTester.newPeer("peer1", 1, 9)
// peer2 := blockPoolTester.newPeer("peer2", 2, 6)
// peer3 := blockPoolTester.newPeer("peer3", 3, 11)
// peer4 := blockPoolTester.newPeer("peer4", 1, 9)
peer2.blocksRequestsMap = peer1.blocksRequestsMap peer2.blocksRequestsMap = peer1.blocksRequestsMap
var expected []int var expected []int
var err error
expected = []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} expected = []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
err = checkStatus(t, blockPool, false, expected) err = checkStatus(nil, blockPool, false, expected)
if err != nil { if err != nil {
return return
} }
peer1.AddPeer() peer1.AddPeer()
expected = []int{0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0} expected = []int{0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0}
err = checkStatus(t, blockPool, true, expected) err = checkStatus(nil, blockPool, true, expected)
if err != nil { if err != nil {
return return
} }
peer1.serveBlocks(8, 9) peer1.serveBlocks(8, 9)
expected = []int{0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0} expected = []int{1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0}
// err = checkStatus(t, blockPool, true, expected) err = checkStatus(nil, blockPool, true, expected)
if err != nil { if err != nil {
return return
} }
peer1.serveBlockHashes(9, 8, 7, 3, 2) peer1.serveBlockHashes(9, 8, 7, 3, 2)
expected = []int{6, 5, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0} expected = []int{6, 5, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0}
// expected = []int{5, 5, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0} err = checkStatus(nil, blockPool, true, expected)
err = checkStatus(t, blockPool, true, expected)
if err != nil { if err != nil {
return return
} }
peer1.serveBlocks(3, 7, 8) peer1.serveBlocks(3, 7, 8)
expected = []int{6, 5, 3, 3, 0, 1, 0, 0, 1, 1, 1, 1, 0} expected = []int{6, 5, 3, 3, 0, 1, 0, 0, 1, 1, 1, 1, 0}
err = checkStatus(t, blockPool, true, expected) err = checkStatus(nil, blockPool, true, expected)
if err != nil { if err != nil {
return return
} }
peer1.serveBlocks(2, 3) peer1.serveBlocks(2, 3)
expected = []int{6, 5, 4, 4, 0, 1, 0, 0, 1, 1, 1, 1, 0} expected = []int{6, 5, 4, 4, 0, 1, 0, 0, 1, 1, 1, 1, 0}
err = checkStatus(t, blockPool, true, expected) err = checkStatus(nil, blockPool, true, expected)
if err != nil { if err != nil {
return return
} }
peer4.AddPeer() peer4.AddPeer()
expected = []int{6, 5, 4, 4, 0, 2, 0, 0, 2, 2, 1, 1, 0} expected = []int{6, 5, 4, 4, 0, 2, 0, 0, 2, 2, 1, 1, 0}
err = checkStatus(t, blockPool, true, expected) err = checkStatus(nil, blockPool, true, expected)
if err != nil { if err != nil {
return return
} }
peer4.sendBlockHashes(12, 11) peer4.sendBlockHashes(12, 11)
expected = []int{6, 5, 4, 4, 0, 2, 0, 0, 2, 2, 1, 1, 0} expected = []int{6, 5, 4, 4, 0, 2, 0, 0, 2, 2, 1, 1, 0}
err = checkStatus(t, blockPool, true, expected) err = checkStatus(nil, blockPool, true, expected)
if err != nil { if err != nil {
return return
} }
peer2.AddPeer() peer2.AddPeer()
expected = []int{6, 5, 4, 4, 0, 3, 0, 0, 3, 3, 1, 2, 0} expected = []int{6, 5, 4, 4, 0, 3, 0, 0, 3, 3, 1, 2, 0}
err = checkStatus(t, blockPool, true, expected) err = checkStatus(nil, blockPool, true, expected)
if err != nil { if err != nil {
return return
} }
@ -153,76 +165,76 @@ func TestBlockPoolStatus(t *testing.T) {
peer2.serveBlocks(5, 6) peer2.serveBlocks(5, 6)
peer2.serveBlockHashes(6, 5, 4, 3, 2) peer2.serveBlockHashes(6, 5, 4, 3, 2)
expected = []int{10, 8, 5, 5, 0, 3, 1, 0, 3, 3, 2, 2, 0} expected = []int{10, 8, 5, 5, 0, 3, 1, 0, 3, 3, 2, 2, 0}
err = checkStatus(t, blockPool, true, expected) err = checkStatus(nil, blockPool, true, expected)
if err != nil { if err != nil {
return return
} }
peer2.serveBlocks(2, 3, 4) peer2.serveBlocks(2, 3, 4)
expected = []int{10, 8, 6, 6, 0, 3, 1, 0, 3, 3, 2, 2, 0} expected = []int{10, 8, 6, 6, 0, 3, 1, 0, 3, 3, 2, 2, 0}
err = checkStatus(t, blockPool, true, expected) err = checkStatus(nil, blockPool, true, expected)
if err != nil { if err != nil {
return return
} }
blockPool.RemovePeer("peer2") blockPool.RemovePeer("peer2")
expected = []int{10, 8, 6, 6, 0, 3, 1, 0, 3, 2, 2, 2, 0} expected = []int{10, 8, 6, 6, 0, 3, 1, 0, 3, 2, 2, 2, 0}
err = checkStatus(t, blockPool, true, expected) err = checkStatus(nil, blockPool, true, expected)
if err != nil { if err != nil {
return return
} }
peer1.serveBlockHashes(2, 1, 0) peer1.serveBlockHashes(2, 1, 0)
expected = []int{11, 9, 6, 6, 0, 3, 1, 0, 3, 2, 2, 2, 0} expected = []int{11, 9, 6, 6, 0, 3, 1, 0, 3, 2, 2, 2, 0}
err = checkStatus(t, blockPool, true, expected) err = checkStatus(nil, blockPool, true, expected)
if err != nil { if err != nil {
return return
} }
peer1.serveBlocks(1, 2) peer1.serveBlocks(1, 2)
expected = []int{11, 9, 7, 7, 0, 3, 1, 0, 3, 2, 2, 2, 0} expected = []int{11, 9, 7, 7, 0, 3, 1, 0, 3, 2, 2, 2, 0}
err = checkStatus(t, blockPool, true, expected) err = checkStatus(nil, blockPool, true, expected)
if err != nil { if err != nil {
return return
} }
peer1.serveBlocks(4, 5) peer1.serveBlocks(4, 5)
expected = []int{11, 9, 8, 8, 0, 3, 1, 0, 3, 2, 2, 2, 0} expected = []int{11, 9, 8, 8, 0, 3, 1, 0, 3, 2, 2, 2, 0}
err = checkStatus(t, blockPool, true, expected) err = checkStatus(nil, blockPool, true, expected)
if err != nil { if err != nil {
return return
} }
peer3.AddPeer() peer3.AddPeer()
expected = []int{11, 9, 8, 8, 0, 4, 1, 0, 4, 3, 2, 3, 0} expected = []int{11, 9, 8, 8, 0, 4, 1, 0, 4, 3, 2, 3, 0}
err = checkStatus(t, blockPool, true, expected) err = checkStatus(nil, blockPool, true, expected)
if err != nil { if err != nil {
return return
} }
peer3.serveBlocks(10, 11) peer3.serveBlocks(10, 11)
expected = []int{12, 9, 9, 9, 0, 4, 1, 0, 4, 3, 3, 3, 0} expected = []int{12, 9, 9, 9, 0, 4, 1, 0, 4, 3, 3, 3, 0}
err = checkStatus(t, blockPool, true, expected) err = checkStatus(nil, blockPool, true, expected)
if err != nil { if err != nil {
return return
} }
peer3.serveBlockHashes(11, 10, 9) peer3.serveBlockHashes(11, 10, 9)
expected = []int{14, 11, 9, 9, 0, 4, 1, 0, 4, 3, 3, 3, 0} expected = []int{14, 11, 9, 9, 0, 4, 1, 0, 4, 3, 3, 3, 0}
err = checkStatus(t, blockPool, true, expected) err = checkStatus(nil, blockPool, true, expected)
if err != nil { if err != nil {
return return
} }
peer4.sendBlocks(11, 12) peer4.sendBlocks(11, 12)
expected = []int{14, 11, 9, 9, 0, 4, 1, 0, 4, 3, 4, 3, 1} expected = []int{14, 11, 9, 9, 0, 4, 1, 0, 4, 3, 4, 3, 1}
err = checkStatus(t, blockPool, true, expected) err = checkStatus(nil, blockPool, true, expected)
if err != nil { if err != nil {
return return
} }
peer3.serveBlocks(9, 10) peer3.serveBlocks(9, 10)
expected = []int{14, 11, 10, 10, 0, 4, 1, 0, 4, 3, 4, 3, 1} expected = []int{14, 11, 10, 10, 0, 4, 1, 0, 4, 3, 4, 3, 1}
err = checkStatus(t, blockPool, true, expected) err = checkStatus(nil, blockPool, true, expected)
if err != nil { if err != nil {
return return
} }
@ -231,10 +243,9 @@ func TestBlockPoolStatus(t *testing.T) {
blockPool.Wait(waitTimeout) blockPool.Wait(waitTimeout)
time.Sleep(200 * time.Millisecond) time.Sleep(200 * time.Millisecond)
expected = []int{14, 3, 11, 3, 8, 4, 1, 8, 4, 3, 4, 3, 1} expected = []int{14, 3, 11, 3, 8, 4, 1, 8, 4, 3, 4, 3, 1}
err = checkStatus(t, blockPool, false, expected) err = checkStatus(nil, blockPool, false, expected)
if err != nil { if err != nil {
return return
} }
return nil
blockPool.Stop()
} }

View file

@ -10,16 +10,20 @@ import (
func CheckInt(name string, got int, expected int, t *testing.T) (err error) { func CheckInt(name string, got int, expected int, t *testing.T) (err error) {
if got != expected { if got != expected {
t.Errorf("status for %v incorrect. expected %v, got %v", name, expected, got) err = fmt.Errorf("status for %v incorrect. expected %v, got %v", name, expected, got)
err = fmt.Errorf("") if t != nil {
t.Error(err)
}
} }
return return
} }
func CheckDuration(name string, got time.Duration, expected time.Duration, t *testing.T) (err error) { func CheckDuration(name string, got time.Duration, expected time.Duration, t *testing.T) (err error) {
if got != expected { if got != expected {
t.Errorf("status for %v incorrect. expected %v, got %v", name, expected, got) err = fmt.Errorf("status for %v incorrect. expected %v, got %v", name, expected, got)
err = fmt.Errorf("") if t != nil {
t.Error(err)
}
} }
return return
} }

View file

@ -61,8 +61,8 @@ func main() {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb := state.New(common.Hash{}, db) statedb := state.New(common.Hash{}, db)
sender := statedb.NewStateObject(common.StringToAddress("sender")) sender := statedb.CreateAccount(common.StringToAddress("sender"))
receiver := statedb.NewStateObject(common.StringToAddress("receiver")) receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
receiver.SetCode(common.Hex2Bytes(*code)) receiver.SetCode(common.Hex2Bytes(*code))
vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(*value)) vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(*value))

View file

@ -2,17 +2,15 @@ package main
import ( import (
"fmt" "fmt"
"net"
"net/http"
"os" "os"
"time" "time"
"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/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/xeth" "github.com/ethereum/go-ethereum/xeth"
"github.com/robertkrimen/otto" "github.com/robertkrimen/otto"
) )
@ -69,14 +67,21 @@ func (js *jsre) startRPC(call otto.FunctionCall) otto.Value {
fmt.Println(err) fmt.Println(err)
return otto.FalseValue() return otto.FalseValue()
} }
dataDir := js.ethereum.DataDir
l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", addr, port)) config := rpc.RpcConfig{
ListenAddress: addr,
ListenPort: uint(port),
// CorsDomain: ctx.GlobalString(RPCCORSDomainFlag.Name),
}
xeth := xeth.New(js.ethereum, nil)
err = rpc.Start(xeth, config)
if err != nil { if err != nil {
fmt.Printf("Can't listen on %s:%d: %v", addr, port, err) fmt.Printf(err.Error())
return otto.FalseValue() return otto.FalseValue()
} }
go http.Serve(l, rpc.JSONRPC(xeth.New(js.ethereum, nil), dataDir))
return otto.TrueValue() return otto.TrueValue()
} }

View file

@ -60,7 +60,7 @@ func runblocktest(ctx *cli.Context) {
// insert the test blocks, which will execute all transactions // insert the test blocks, which will execute all transactions
chain := ethereum.ChainManager() chain := ethereum.ChainManager()
if err := chain.InsertChain(test.Blocks); err != nil { if err := chain.InsertChain(test.Blocks); err != nil {
utils.Fatalf("Block Test load error: %v", err) utils.Fatalf("Block Test load error: %v %T", err, err)
} else { } else {
fmt.Println("Block Test chain loaded") fmt.Println("Block Test chain loaded")
} }

View file

@ -67,14 +67,14 @@ type jsre struct {
prompter prompter
} }
func newJSRE(ethereum *eth.Ethereum, libPath string) *jsre { func newJSRE(ethereum *eth.Ethereum, libPath string, interactive bool) *jsre {
js := &jsre{ethereum: ethereum, ps1: "> "} js := &jsre{ethereum: ethereum, ps1: "> "}
js.xeth = xeth.New(ethereum, js) js.xeth = xeth.New(ethereum, js)
js.re = re.New(libPath) js.re = re.New(libPath)
js.apiBindings() js.apiBindings()
js.adminBindings() js.adminBindings()
if !liner.TerminalSupported() { if !liner.TerminalSupported() || !interactive {
js.prompter = dumbterm{bufio.NewReader(os.Stdin)} js.prompter = dumbterm{bufio.NewReader(os.Stdin)}
} else { } else {
lr := liner.NewLiner() lr := liner.NewLiner()
@ -91,8 +91,7 @@ func newJSRE(ethereum *eth.Ethereum, libPath string) *jsre {
func (js *jsre) apiBindings() { func (js *jsre) apiBindings() {
ethApi := rpc.NewEthereumApi(js.xeth, js.ethereum.DataDir) ethApi := rpc.NewEthereumApi(js.xeth)
ethApi.Close()
//js.re.Bind("jeth", rpc.NewJeth(ethApi, js.re.ToVal)) //js.re.Bind("jeth", rpc.NewJeth(ethApi, js.re.ToVal))
jeth := rpc.NewJeth(ethApi, js.re.ToVal, js.re) jeth := rpc.NewJeth(ethApi, js.re.ToVal, js.re)
@ -102,7 +101,7 @@ func (js *jsre) apiBindings() {
jethObj := t.Object() jethObj := t.Object()
jethObj.Set("send", jeth.Send) jethObj.Set("send", jeth.Send)
err := js.re.Compile("bignum.js", re.BigNumber_JS) err := js.re.Compile("bignumber.js", re.BigNumber_JS)
if err != nil { if err != nil {
utils.Fatalf("Error loading bignumber.js: %v", err) utils.Fatalf("Error loading bignumber.js: %v", err)
} }

View file

@ -2,6 +2,7 @@ package main
import ( import (
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path" "path"
"testing" "testing"
@ -9,7 +10,6 @@ import (
"github.com/robertkrimen/otto" "github.com/robertkrimen/otto"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"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"
) )
@ -30,8 +30,8 @@ func testJEthRE(t *testing.T) (repl *jsre, ethereum *eth.Ethereum, err error) {
} }
// FIXME: this does not work ATM // FIXME: this does not work ATM
ks := crypto.NewKeyStorePlain("/tmp/eth/keys") ks := crypto.NewKeyStorePlain("/tmp/eth/keys")
common.WriteFile("/tmp/eth/keys/e273f01c99144c438695e10f24926dc1f9fbf62d/e273f01c99144c438695e10f24926dc1f9fbf62d", ioutil.WriteFile("/tmp/eth/keys/e273f01c99144c438695e10f24926dc1f9fbf62d/e273f01c99144c438695e10f24926dc1f9fbf62d",
[]byte(`{"Id":"RhRXD+fNRKS4jx+7ZfEsNA==","Address":"4nPwHJkUTEOGleEPJJJtwfn79i0=","PrivateKey":"h4ACVpe74uIvi5Cg/2tX/Yrm2xdr3J7QoMbMtNX2CNc="}`)) []byte(`{"Id":"RhRXD+fNRKS4jx+7ZfEsNA==","Address":"4nPwHJkUTEOGleEPJJJtwfn79i0=","PrivateKey":"h4ACVpe74uIvi5Cg/2tX/Yrm2xdr3J7QoMbMtNX2CNc="}`), os.ModePerm)
port++ port++
ethereum, err = eth.New(&eth.Config{ ethereum, err = eth.New(&eth.Config{
@ -47,7 +47,7 @@ func testJEthRE(t *testing.T) (repl *jsre, ethereum *eth.Ethereum, err error) {
return return
} }
assetPath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext") assetPath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "cmd", "mist", "assets", "ext")
repl = newJSRE(ethereum, assetPath) repl = newJSRE(ethereum, assetPath, false)
return return
} }

View file

@ -23,14 +23,15 @@ package main
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"runtime" "runtime"
"strconv" "strconv"
"strings"
"time" "time"
"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/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/core/state" "github.com/ethereum/go-ethereum/core/state"
@ -41,8 +42,8 @@ import (
) )
const ( const (
ClientIdentifier = "Ethereum(G)" ClientIdentifier = "Geth"
Version = "0.9.3" Version = "0.9.4"
) )
var ( var (
@ -74,10 +75,44 @@ Regular users do not need to execute it.
The output of this command is supposed to be machine-readable. The output of this command is supposed to be machine-readable.
`, `,
}, },
{
Name: "wallet",
Usage: "ethereum presale wallet",
Subcommands: []cli.Command{
{
Action: importWallet,
Name: "import",
Usage: "import ethereum presale wallet",
},
},
},
{ {
Action: accountList, Action: accountList,
Name: "account", Name: "account",
Usage: "manage accounts", Usage: "manage accounts",
Description: `
Manage accounts lets you create new accounts, list all existing accounts,
import a private key into a new account.
It supports interactive mode, when you are prompted for password as well as
non-interactive mode where passwords are supplied via a given password file.
Non-interactive mode is only meant for scripted use on test networks or known
safe environments.
Make sure you remember the password you gave when creating a new account (with
either new or import). Without it you are not able to unlock your account.
Note that exporting your key in unencrypted format is NOT supported.
Keys are stored under <DATADIR>/keys.
It is safe to transfer the entire directory or the individual keys therein
between ethereum nodes.
Make sure you backup your keys regularly.
And finally. DO NOT FORGET YOUR PASSWORD.
`,
Subcommands: []cli.Command{ Subcommands: []cli.Command{
{ {
Action: accountList, Action: accountList,
@ -88,6 +123,51 @@ The output of this command is supposed to be machine-readable.
Action: accountCreate, Action: accountCreate,
Name: "new", Name: "new",
Usage: "create a new account", Usage: "create a new account",
Description: `
ethereum account new
Creates a new account. Prints the address.
The account is saved in encrypted format, you are prompted for a passphrase.
You must remember this passphrase to unlock your account in the future.
For non-interactive use the passphrase can be specified with the --password flag:
ethereum --password <passwordfile> account new
Note, this is meant to be used for testing only, it is a bad idea to save your
password to file or expose in any other way.
`,
},
{
Action: accountImport,
Name: "import",
Usage: "import a private key into a new account",
Description: `
ethereum account import <keyfile>
Imports an unencrypted private key from <keyfile> and creates a new account.
Prints the address.
The keyfile is assumed to contain an unencrypted private key in canonical EC
raw bytes format.
The account is saved in encrypted format, you are prompted for a passphrase.
You must remember this passphrase to unlock your account in the future.
For non-interactive use the passphrase can be specified with the -password flag:
ethereum --password <passwordfile> account import <keyfile>
Note:
As you can directly copy your encrypted accounts to another ethereum instance,
this import mechanism is not needed when you transfer an account between
nodes.
`,
}, },
}, },
}, },
@ -103,18 +183,20 @@ Use "ethereum dump 0" to dump the genesis block.
{ {
Action: console, Action: console,
Name: "console", Name: "console",
Usage: `Ethereum Console: interactive JavaScript environment`, Usage: `Geth Console: interactive JavaScript environment`,
Description: ` Description: `
Console is an interactive shell for the Ethereum JavaScript runtime environment which exposes a node admin interface as well as the DAPP JavaScript API. The Geth console is an interactive shell for the JavaScript runtime environment
which exposes a node admin interface as well as the DAPP JavaScript API.
See https://github.com/ethereum/go-ethereum/wiki/Frontier-Console See https://github.com/ethereum/go-ethereum/wiki/Frontier-Console
`, `,
}, },
{ {
Action: execJSFiles, Action: execJSFiles,
Name: "js", Name: "js",
Usage: `executes the given JavaScript files in the Ethereum Frontier JavaScript VM`, Usage: `executes the given JavaScript files in the Geth JavaScript VM`,
Description: ` Description: `
The Ethereum JavaScript VM exposes a node admin interface as well as the DAPP JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Frontier-Console The JavaScript VM exposes a node admin interface as well as the DAPP
JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console
`, `,
}, },
{ {
@ -130,6 +212,7 @@ The Ethereum JavaScript VM exposes a node admin interface as well as the DAPP Ja
} }
app.Flags = []cli.Flag{ app.Flags = []cli.Flag{
utils.UnlockedAccountFlag, utils.UnlockedAccountFlag,
utils.PasswordFileFlag,
utils.BootnodesFlag, utils.BootnodesFlag,
utils.DataDirFlag, utils.DataDirFlag,
utils.JSpathFlag, utils.JSpathFlag,
@ -138,6 +221,7 @@ The Ethereum JavaScript VM exposes a node admin interface as well as the DAPP Ja
utils.LogJSONFlag, utils.LogJSONFlag,
utils.LogLevelFlag, utils.LogLevelFlag,
utils.MaxPeersFlag, utils.MaxPeersFlag,
utils.EtherbaseFlag,
utils.MinerThreadsFlag, utils.MinerThreadsFlag,
utils.MiningEnabledFlag, utils.MiningEnabledFlag,
utils.NATFlag, utils.NATFlag,
@ -146,10 +230,10 @@ The Ethereum JavaScript VM exposes a node admin interface as well as the DAPP Ja
utils.RPCEnabledFlag, utils.RPCEnabledFlag,
utils.RPCListenAddrFlag, utils.RPCListenAddrFlag,
utils.RPCPortFlag, utils.RPCPortFlag,
utils.UnencryptedKeysFlag,
utils.VMDebugFlag, utils.VMDebugFlag,
utils.ProtocolVersionFlag, utils.ProtocolVersionFlag,
utils.NetworkIdFlag, utils.NetworkIdFlag,
utils.RPCCORSDomainFlag,
} }
// missing: // missing:
@ -194,7 +278,7 @@ func console(ctx *cli.Context) {
} }
startEth(ctx, ethereum) startEth(ctx, ethereum)
repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name)) repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name), true)
repl.interactive() repl.interactive()
ethereum.Stop() ethereum.Stop()
@ -209,7 +293,7 @@ func execJSFiles(ctx *cli.Context) {
} }
startEth(ctx, ethereum) startEth(ctx, ethereum)
repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name)) repl := newJSRE(ethereum, ctx.String(utils.JSpathFlag.Name), false)
for _, file := range ctx.Args() { for _, file := range ctx.Args() {
repl.exec(file) repl.exec(file)
} }
@ -218,22 +302,36 @@ func execJSFiles(ctx *cli.Context) {
ethereum.WaitForShutdown() ethereum.WaitForShutdown()
} }
func startEth(ctx *cli.Context, eth *eth.Ethereum) { func unlockAccount(ctx *cli.Context, am *accounts.Manager, account string) (passphrase string) {
utils.StartEthereum(eth) var err error
// Load startup keys. XXX we are going to need a different format // Load startup keys. XXX we are going to need a different format
account := ctx.GlobalString(utils.UnlockedAccountFlag.Name)
if len(account) > 0 {
split := strings.Split(account, ":")
if len(split) != 2 {
utils.Fatalf("Illegal 'unlock' format (address:password)")
}
am := eth.AccountManager()
// Attempt to unlock the account // Attempt to unlock the account
err := am.Unlock(common.FromHex(split[0]), split[1]) passphrase = getPassPhrase(ctx, "", false)
accbytes := common.FromHex(account)
if len(accbytes) == 0 {
utils.Fatalf("Invalid account address '%s'", account)
}
err = am.Unlock(accbytes, passphrase)
if err != nil { if err != nil {
utils.Fatalf("Unlock account failed '%v'", err) utils.Fatalf("Unlock account failed '%v'", err)
} }
return
}
func startEth(ctx *cli.Context, eth *eth.Ethereum) {
utils.StartEthereum(eth)
am := eth.AccountManager()
account := ctx.GlobalString(utils.UnlockedAccountFlag.Name)
if len(account) > 0 {
if account == "primary" {
accbytes, err := am.Primary()
if err != nil {
utils.Fatalf("no primary account: %v", err)
}
account = common.ToHex(accbytes)
}
unlockAccount(ctx, am, account)
} }
// Start auxiliary services if enabled. // Start auxiliary services if enabled.
if ctx.GlobalBool(utils.RPCEnabledFlag.Name) { if ctx.GlobalBool(utils.RPCEnabledFlag.Name) {
@ -255,16 +353,15 @@ func accountList(ctx *cli.Context) {
} }
} }
func accountCreate(ctx *cli.Context) { func getPassPhrase(ctx *cli.Context, desc string, confirmation bool) (passphrase string) {
am := utils.GetAccountManager(ctx) passfile := ctx.GlobalString(utils.PasswordFileFlag.Name)
passphrase := "" if len(passfile) == 0 {
if !ctx.GlobalBool(utils.UnencryptedKeysFlag.Name) { fmt.Println(desc)
fmt.Println("The new account will be encrypted with a passphrase.")
fmt.Println("Please enter a passphrase now.")
auth, err := readPassword("Passphrase: ", true) auth, err := readPassword("Passphrase: ", true)
if err != nil { if err != nil {
utils.Fatalf("%v", err) utils.Fatalf("%v", err)
} }
if confirmation {
confirm, err := readPassword("Repeat Passphrase: ", false) confirm, err := readPassword("Repeat Passphrase: ", false)
if err != nil { if err != nil {
utils.Fatalf("%v", err) utils.Fatalf("%v", err)
@ -272,13 +369,61 @@ func accountCreate(ctx *cli.Context) {
if auth != confirm { if auth != confirm {
utils.Fatalf("Passphrases did not match.") utils.Fatalf("Passphrases did not match.")
} }
passphrase = auth
} }
passphrase = auth
} else {
passbytes, err := ioutil.ReadFile(passfile)
if err != nil {
utils.Fatalf("Unable to read password file '%s': %v", passfile, err)
}
passphrase = string(passbytes)
}
return
}
func accountCreate(ctx *cli.Context) {
am := utils.GetAccountManager(ctx)
passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true)
acct, err := am.NewAccount(passphrase) acct, err := am.NewAccount(passphrase)
if err != nil { if err != nil {
utils.Fatalf("Could not create the account: %v", err) utils.Fatalf("Could not create the account: %v", err)
} }
fmt.Printf("Address: %x\n", acct.Address) fmt.Printf("Address: %x\n", acct)
}
func importWallet(ctx *cli.Context) {
keyfile := ctx.Args().First()
if len(keyfile) == 0 {
utils.Fatalf("keyfile must be given as argument")
}
keyJson, err := ioutil.ReadFile(keyfile)
if err != nil {
utils.Fatalf("Could not read wallet file: %v", err)
}
am := utils.GetAccountManager(ctx)
passphrase := getPassPhrase(ctx, "", false)
acct, err := am.ImportPreSaleKey(keyJson, passphrase)
if err != nil {
utils.Fatalf("Could not create the account: %v", err)
}
fmt.Printf("Address: %x\n", acct)
}
func accountImport(ctx *cli.Context) {
keyfile := ctx.Args().First()
if len(keyfile) == 0 {
utils.Fatalf("keyfile must be given as argument")
}
am := utils.GetAccountManager(ctx)
passphrase := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true)
acct, err := am.Import(keyfile, passphrase)
if err != nil {
utils.Fatalf("Could not create the account: %v", err)
}
fmt.Printf("Address: %x\n", acct)
} }
func importchain(ctx *cli.Context) { func importchain(ctx *cli.Context) {
@ -325,7 +470,6 @@ func dump(ctx *cli.Context) {
} else { } else {
statedb := state.New(block.Root(), stateDb) statedb := state.New(block.Root(), stateDb)
fmt.Printf("%s\n", statedb.Dump()) fmt.Printf("%s\n", statedb.Dump())
// fmt.Println(block)
} }
} }
} }

View file

@ -70,16 +70,16 @@
var address = localStorage.getItem("address"); var address = localStorage.getItem("address");
// deploy if not exist // deploy if not exist
if (address == null) { if(address === null) {
var code = "0x60056013565b61014f8061003a6000396000f35b620f42406000600033600160a060020a0316815260200190815260200160002081905550560060e060020a600035048063d0679d3414610020578063e3d670d71461003457005b61002e600435602435610049565b60006000f35b61003f600435610129565b8060005260206000f35b806000600033600160a060020a03168152602001908152602001600020541061007157610076565b610125565b806000600033600160a060020a03168152602001908152602001600020908154039081905550806000600084600160a060020a031681526020019081526020016000209081540190819055508033600160a060020a03167fb52dda022b6c1a1f40905a85f257f689aa5d69d850e49cf939d688fbe5af594660006000a38082600160a060020a03167fb52dda022b6c1a1f40905a85f257f689aa5d69d850e49cf939d688fbe5af594660006000a35b5050565b60006000600083600160a060020a0316815260200190815260200160002054905091905056"; var code = "0x60056013565b61014f8061003a6000396000f35b620f42406000600033600160a060020a0316815260200190815260200160002081905550560060e060020a600035048063d0679d3414610020578063e3d670d71461003457005b61002e600435602435610049565b60006000f35b61003f600435610129565b8060005260206000f35b806000600033600160a060020a03168152602001908152602001600020541061007157610076565b610125565b806000600033600160a060020a03168152602001908152602001600020908154039081905550806000600084600160a060020a031681526020019081526020016000209081540190819055508033600160a060020a03167fb52dda022b6c1a1f40905a85f257f689aa5d69d850e49cf939d688fbe5af594660006000a38082600160a060020a03167fb52dda022b6c1a1f40905a85f257f689aa5d69d850e49cf939d688fbe5af594660006000a35b5050565b60006000600083600160a060020a0316815260200190815260200160002054905091905056";
address = web3.eth.transact({from: eth.coinbase, data: code}); address = web3.eth.transact({from: eth.coinbase, data: code, gas: "1000000"});
localStorage.setItem("address", address); localStorage.setItem("address", address);
} }
document.querySelector("#contract_addr").innerHTML = address; document.querySelector("#contract_addr").innerHTML = address;
var Contract = web3.eth.contract(desc); var Contract = web3.eth.contract(desc);
contract = new Contract(address); contract = new Contract(address);
contract.Changed({from: eth.coinbase}).changed(function() { contract.Changed({from: eth.accounts[0]}).changed(function() {
refresh(); refresh();
}); });
@ -109,7 +109,7 @@
var amount = parseInt( value.value ); var amount = parseInt( value.value );
console.log("transact: ", to.value, " => ", amount) console.log("transact: ", to.value, " => ", amount)
contract.send( to.value, amount ); contract.sendTransaction({from: eth.accounts[0]}).send( to.value, amount );
to.value = ""; to.value = "";
value.value = ""; value.value = "";

@ -1 +1 @@
Subproject commit 9f073d9091cd2d2b46dd46afea9dcf5d825ecd7c Subproject commit 2536888f817a1a15b05dab4727d30f73d6763f07

View file

@ -22,13 +22,14 @@ package main
import ( import (
"encoding/json" "encoding/json"
"io/ioutil"
"os" "os"
"strconv" "strconv"
"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/core/types"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
) )
type plugin struct { type plugin struct {
@ -46,14 +47,14 @@ func (self *Gui) AddPlugin(pluginPath string) {
self.plugins[pluginPath] = plugin{Name: pluginPath, Path: pluginPath} self.plugins[pluginPath] = plugin{Name: pluginPath, Path: pluginPath}
json, _ := json.MarshalIndent(self.plugins, "", " ") json, _ := json.MarshalIndent(self.plugins, "", " ")
common.WriteFile(self.eth.DataDir+"/plugins.json", json) ioutil.WriteFile(self.eth.DataDir+"/plugins.json", json, os.ModePerm)
} }
func (self *Gui) RemovePlugin(pluginPath string) { func (self *Gui) RemovePlugin(pluginPath string) {
delete(self.plugins, pluginPath) delete(self.plugins, pluginPath)
json, _ := json.MarshalIndent(self.plugins, "", " ") json, _ := json.MarshalIndent(self.plugins, "", " ")
common.WriteFile(self.eth.DataDir+"/plugins.json", json) ioutil.WriteFile(self.eth.DataDir+"/plugins.json", json, os.ModePerm)
} }
func (self *Gui) DumpState(hash, path string) { func (self *Gui) DumpState(hash, path string) {

View file

@ -25,6 +25,7 @@ import "C"
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"math/big" "math/big"
"path" "path"
"runtime" "runtime"
@ -91,8 +92,8 @@ func NewWindow(ethereum *eth.Ethereum) *Gui {
plugins: make(map[string]plugin), plugins: make(map[string]plugin),
serviceEvents: make(chan ServEv, 1), serviceEvents: make(chan ServEv, 1),
} }
data, _ := common.ReadAllFile(path.Join(ethereum.DataDir, "plugins.json")) data, _ := ioutil.ReadFile(path.Join(ethereum.DataDir, "plugins.json"))
json.Unmarshal([]byte(data), &gui.plugins) json.Unmarshal(data, &gui.plugins)
return gui return gui
} }

View file

@ -47,12 +47,19 @@ var (
Usage: "absolute path to GUI assets directory", Usage: "absolute path to GUI assets directory",
Value: common.DefaultAssetPath(), Value: common.DefaultAssetPath(),
} }
rpcCorsFlag = utils.RPCCORSDomainFlag
) )
func init() { func init() {
// Mist-specific default
if len(rpcCorsFlag.Value) == 0 {
rpcCorsFlag.Value = "http://localhost"
}
app.Action = run app.Action = run
app.Flags = []cli.Flag{ app.Flags = []cli.Flag{
assetPathFlag, assetPathFlag,
rpcCorsFlag,
utils.BootnodesFlag, utils.BootnodesFlag,
utils.DataDirFlag, utils.DataDirFlag,

View file

@ -2,9 +2,6 @@ package utils
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"fmt"
"net"
"net/http"
"os" "os"
"path" "path"
"runtime" "runtime"
@ -54,7 +51,7 @@ func NewApp(version, usage string) *cli.App {
app := cli.NewApp() app := cli.NewApp()
app.Name = path.Base(os.Args[0]) app.Name = path.Base(os.Args[0])
app.Author = "" app.Author = ""
app.Authors = nil //app.Authors = nil
app.Email = "" app.Email = ""
app.Version = version app.Version = version
app.Usage = usage app.Usage = usage
@ -96,15 +93,21 @@ var (
Name: "mine", Name: "mine",
Usage: "Enable mining", Usage: "Enable mining",
} }
EtherbaseFlag = cli.StringFlag{
// key settings Name: "etherbase",
UnencryptedKeysFlag = cli.BoolFlag{ Usage: "public address for block mining rewards. By default the address of your primary account is used",
Name: "unencrypted-keys", Value: "primary",
Usage: "disable private key disk encryption (for testing)",
} }
UnlockedAccountFlag = cli.StringFlag{ UnlockedAccountFlag = cli.StringFlag{
Name: "unlock", Name: "unlock",
Usage: "Unlock a given account untill this programs exits (address:password)", Usage: "unlock the account given until this program exits (prompts for password). '--unlock primary' unlocks the primary account",
Value: "",
}
PasswordFileFlag = cli.StringFlag{
Name: "password",
Usage: "Path to password file for (un)locking an existing account.",
Value: "",
} }
// logging and debug settings // logging and debug settings
@ -142,7 +145,11 @@ var (
Usage: "Port on which the JSON-RPC server should listen", Usage: "Port on which the JSON-RPC server should listen",
Value: 8545, Value: 8545,
} }
RPCCORSDomainFlag = cli.StringFlag{
Name: "rpccorsdomain",
Usage: "Domain on which to send Access-Control-Allow-Origin header",
Value: "",
}
// Network Settings // Network Settings
MaxPeersFlag = cli.IntFlag{ MaxPeersFlag = cli.IntFlag{
Name: "maxpeers", Name: "maxpeers",
@ -214,6 +221,7 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
LogFile: ctx.GlobalString(LogFileFlag.Name), LogFile: ctx.GlobalString(LogFileFlag.Name),
LogLevel: ctx.GlobalInt(LogLevelFlag.Name), LogLevel: ctx.GlobalInt(LogLevelFlag.Name),
LogJSON: ctx.GlobalString(LogJSONFlag.Name), LogJSON: ctx.GlobalString(LogJSONFlag.Name),
Etherbase: ctx.GlobalString(EtherbaseFlag.Name),
MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name), MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name),
AccountManager: GetAccountManager(ctx), AccountManager: GetAccountManager(ctx),
VmDebug: ctx.GlobalBool(VMDebugFlag.Name), VmDebug: ctx.GlobalBool(VMDebugFlag.Name),
@ -243,23 +251,17 @@ func GetChain(ctx *cli.Context) (*core.ChainManager, common.Database, common.Dat
func GetAccountManager(ctx *cli.Context) *accounts.Manager { func GetAccountManager(ctx *cli.Context) *accounts.Manager {
dataDir := ctx.GlobalString(DataDirFlag.Name) dataDir := ctx.GlobalString(DataDirFlag.Name)
var ks crypto.KeyStore2 ks := crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys"))
if ctx.GlobalBool(UnencryptedKeysFlag.Name) {
ks = crypto.NewKeyStorePlain(path.Join(dataDir, "plainkeys"))
} else {
ks = crypto.NewKeyStorePassphrase(path.Join(dataDir, "keys"))
}
return accounts.NewManager(ks) return accounts.NewManager(ks)
} }
func StartRPC(eth *eth.Ethereum, ctx *cli.Context) { func StartRPC(eth *eth.Ethereum, ctx *cli.Context) {
addr := ctx.GlobalString(RPCListenAddrFlag.Name) config := rpc.RpcConfig{
port := ctx.GlobalInt(RPCPortFlag.Name) ListenAddress: ctx.GlobalString(RPCListenAddrFlag.Name),
dataDir := ctx.GlobalString(DataDirFlag.Name) ListenPort: uint(ctx.GlobalInt(RPCPortFlag.Name)),
fmt.Println("Starting RPC on port: ", port) CorsDomain: ctx.GlobalString(RPCCORSDomainFlag.Name),
l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", addr, port))
if err != nil {
Fatalf("Can't listen on %s:%d: %v", addr, port, err)
} }
go http.Serve(l, rpc.JSONRPC(xeth.New(eth, nil), dataDir))
xeth := xeth.New(eth, nil)
_ = rpc.Start(xeth, config)
} }

View file

@ -2,7 +2,6 @@ package common
import ( import (
"fmt" "fmt"
"io/ioutil"
"os" "os"
"os/user" "os/user"
"path" "path"
@ -43,35 +42,6 @@ func FileExist(filePath string) bool {
return true return true
} }
func ReadAllFile(filePath string) (string, error) {
file, err := os.Open(filePath)
if err != nil {
return "", err
}
data, err := ioutil.ReadAll(file)
if err != nil {
return "", err
}
return string(data), nil
}
func WriteFile(filePath string, content []byte) error {
fh, err := os.OpenFile(filePath, os.O_TRUNC|os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil {
return err
}
defer fh.Close()
_, err = fh.Write(content)
if err != nil {
return err
}
return nil
}
func AbsolutePath(Datadir string, filename string) string { func AbsolutePath(Datadir string, filename string) string {
if path.IsAbs(filename) { if path.IsAbs(filename) {
return filename return filename

View file

@ -2,56 +2,11 @@ package common
import ( import (
"os" "os"
"testing" // "testing"
checker "gopkg.in/check.v1" checker "gopkg.in/check.v1"
) )
func TestGoodFile(t *testing.T) {
goodpath := "~/goethereumtest.pass"
path := ExpandHomePath(goodpath)
contentstring := "3.14159265358979323846"
err := WriteFile(path, []byte(contentstring))
if err != nil {
t.Error("Could not write file")
}
if !FileExist(path) {
t.Error("File not found at", path)
}
v, err := ReadAllFile(path)
if err != nil {
t.Error("Could not read file", path)
}
if v != contentstring {
t.Error("Expected", contentstring, "Got", v)
}
}
func TestBadFile(t *testing.T) {
badpath := "/this/path/should/not/exist/goethereumtest.fail"
path := ExpandHomePath(badpath)
contentstring := "3.14159265358979323846"
err := WriteFile(path, []byte(contentstring))
if err == nil {
t.Error("Wrote file, but should not be able to", path)
}
if FileExist(path) {
t.Error("Found file, but should not be able to", path)
}
v, err := ReadAllFile(path)
if err == nil {
t.Error("Read file, but should not be able to", v)
}
}
type CommonSuite struct{} type CommonSuite struct{}
var _ = checker.Suite(&CommonSuite{}) var _ = checker.Suite(&CommonSuite{})

View file

@ -11,6 +11,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"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/params"
"github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/pow"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"gopkg.in/fatih/set.v0" "gopkg.in/fatih/set.v0"
@ -83,7 +84,7 @@ func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, stated
} }
// Update the state with pending changes // Update the state with pending changes
statedb.Update(nil) statedb.Update()
cumulative := new(big.Int).Set(usedGas.Add(usedGas, gas)) cumulative := new(big.Int).Set(usedGas.Add(usedGas, gas))
receipt := types.NewReceipt(statedb.Root().Bytes(), cumulative) receipt := types.NewReceipt(statedb.Root().Bytes(), cumulative)
@ -210,14 +211,16 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big
return return
} }
// Accumulate static rewards; block reward, uncle's and uncle inclusion. // Verify uncles
if err = sm.AccumulateRewards(state, block, parent); err != nil { if err = sm.VerifyUncles(state, block, parent); err != nil {
return return
} }
// Accumulate static rewards; block reward, uncle's and uncle inclusion.
AccumulateRewards(state, block)
// Commit state objects/accounts to a temporary trie (does not save) // Commit state objects/accounts to a temporary trie (does not save)
// used to calculate the state root. // used to calculate the state root.
state.Update(common.Big0) state.Update()
if header.Root != state.Root() { if header.Root != state.Root() {
err = fmt.Errorf("invalid merkle root. received=%x got=%x", header.Root, state.Root()) err = fmt.Errorf("invalid merkle root. received=%x got=%x", header.Root, state.Root())
return return
@ -233,8 +236,9 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big
sm.txpool.RemoveSet(block.Transactions()) sm.txpool.RemoveSet(block.Transactions())
} }
for _, tx := range block.Transactions() { // This puts transactions in a extra db for rpc
putTx(sm.extraDb, tx) for i, tx := range block.Transactions() {
putTx(sm.extraDb, tx, block, uint64(i))
} }
if uncle { if uncle {
@ -251,7 +255,7 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (td *big
// an uncle or anything that isn't on the current block chain. // an uncle or anything that isn't on the current block chain.
// Validation validates easy over difficult (dagger takes longer time = difficult) // Validation validates easy over difficult (dagger takes longer time = difficult)
func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header) error { func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header) error {
if len(block.Extra) > 1024 { if big.NewInt(int64(len(block.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
return fmt.Errorf("Block extra data too long (%d)", len(block.Extra)) return fmt.Errorf("Block extra data too long (%d)", len(block.Extra))
} }
@ -260,10 +264,11 @@ func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header) error {
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd) return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)
} }
// block.gasLimit - parent.gasLimit <= parent.gasLimit / 1024 // block.gasLimit - parent.gasLimit <= parent.gasLimit / GasLimitBoundDivisor
a := new(big.Int).Sub(block.GasLimit, parent.GasLimit) a := new(big.Int).Sub(block.GasLimit, parent.GasLimit)
b := new(big.Int).Div(parent.GasLimit, big.NewInt(1024)) a.Abs(a)
if a.Cmp(b) > 0 { b := new(big.Int).Div(parent.GasLimit, params.GasLimitBoundDivisor)
if !(a.Cmp(b) < 0) || (block.GasLimit.Cmp(params.MinGasLimit) == -1) {
return fmt.Errorf("GasLimit check failed for block %v (%v > %v)", block.GasLimit, a, b) return fmt.Errorf("GasLimit check failed for block %v (%v > %v)", block.GasLimit, a, b)
} }
@ -287,9 +292,27 @@ func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header) error {
return nil return nil
} }
func (sm *BlockProcessor) AccumulateRewards(statedb *state.StateDB, block, parent *types.Block) error { func AccumulateRewards(statedb *state.StateDB, block *types.Block) {
reward := new(big.Int).Set(BlockReward) reward := new(big.Int).Set(BlockReward)
for _, uncle := range block.Uncles() {
num := new(big.Int).Add(big.NewInt(8), uncle.Number)
num.Sub(num, block.Number())
r := new(big.Int)
r.Mul(BlockReward, num)
r.Div(r, big.NewInt(8))
statedb.AddBalance(uncle.Coinbase, r)
reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32)))
}
// Get the account associated with the coinbase
statedb.AddBalance(block.Header().Coinbase, reward)
}
func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *types.Block) error {
ancestors := set.New() ancestors := set.New()
uncles := set.New() uncles := set.New()
ancestorHeaders := make(map[common.Hash]*types.Header) ancestorHeaders := make(map[common.Hash]*types.Header)
@ -323,17 +346,8 @@ func (sm *BlockProcessor) AccumulateRewards(statedb *state.StateDB, block, paren
return ValidationError(fmt.Sprintf("%v", err)) return ValidationError(fmt.Sprintf("%v", err))
} }
r := new(big.Int)
r.Mul(BlockReward, big.NewInt(15)).Div(r, big.NewInt(16))
statedb.AddBalance(uncle.Coinbase, r)
reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32)))
} }
// Get the account associated with the coinbase
statedb.AddBalance(block.Header().Coinbase, reward)
return nil return nil
} }
@ -350,16 +364,30 @@ func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err erro
) )
sm.TransitionState(state, parent, block, true) sm.TransitionState(state, parent, block, true)
sm.AccumulateRewards(state, block, parent)
return state.Logs(), nil return state.Logs(), nil
} }
func putTx(db common.Database, tx *types.Transaction) { func putTx(db common.Database, tx *types.Transaction, block *types.Block, i uint64) {
rlpEnc, err := rlp.EncodeToBytes(tx) rlpEnc, err := rlp.EncodeToBytes(tx)
if err != nil { if err != nil {
statelogger.Infoln("Failed encoding tx", err) statelogger.Infoln("Failed encoding tx", err)
return return
} }
db.Put(tx.Hash().Bytes(), rlpEnc) db.Put(tx.Hash().Bytes(), rlpEnc)
var txExtra struct {
BlockHash common.Hash
BlockIndex uint64
Index uint64
}
txExtra.BlockHash = block.Hash()
txExtra.BlockIndex = block.NumberU64()
txExtra.Index = i
rlpMeta, err := rlp.EncodeToBytes(txExtra)
if err != nil {
statelogger.Infoln("Failed encoding meta", err)
return
}
db.Put(append(tx.Hash().Bytes(), 0x0001), rlpMeta)
} }

View file

@ -5,10 +5,10 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/pow"
"github.com/ethereum/go-ethereum/core/state"
) )
// So we can generate blocks easily // So we can generate blocks easily
@ -81,7 +81,7 @@ func makeBlock(bman *BlockProcessor, parent *types.Block, i int, db common.Datab
cbase := state.GetOrNewStateObject(addr) cbase := state.GetOrNewStateObject(addr)
cbase.SetGasPool(CalcGasLimit(parent, block)) cbase.SetGasPool(CalcGasLimit(parent, block))
cbase.AddBalance(BlockReward) cbase.AddBalance(BlockReward)
state.Update(common.Big0) state.Update()
block.SetRoot(state.Root()) block.SetRoot(state.Root())
return block return block
} }

View file

@ -12,6 +12,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"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/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
@ -32,18 +33,15 @@ type StateQuery interface {
func CalcDifficulty(block, parent *types.Header) *big.Int { func CalcDifficulty(block, parent *types.Header) *big.Int {
diff := new(big.Int) diff := new(big.Int)
diffBoundDiv := big.NewInt(2048) adjust := new(big.Int).Div(parent.Difficulty, params.DifficultyBoundDivisor)
min := big.NewInt(131072) if big.NewInt(int64(block.Time)-int64(parent.Time)).Cmp(params.DurationLimit) < 0 {
adjust := new(big.Int).Div(parent.Difficulty, diffBoundDiv)
if (block.Time - parent.Time) < 8 {
diff.Add(parent.Difficulty, adjust) diff.Add(parent.Difficulty, adjust)
} else { } else {
diff.Sub(parent.Difficulty, adjust) diff.Sub(parent.Difficulty, adjust)
} }
if diff.Cmp(min) < 0 { if diff.Cmp(params.MinimumDifficulty) < 0 {
return min return params.MinimumDifficulty
} }
return diff return diff
@ -76,7 +74,7 @@ func CalcGasLimit(parent, block *types.Block) *big.Int {
result := new(big.Int).Add(previous, curInt) result := new(big.Int).Add(previous, curInt)
result.Div(result, big.NewInt(1024)) result.Div(result, big.NewInt(1024))
return common.BigMax(GenesisGasLimit, result) return common.BigMax(params.GenesisGasLimit, result)
} }
type ChainManager struct { type ChainManager struct {

View file

@ -8,17 +8,22 @@ import (
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
) )
type Execution struct { type Execution struct {
env vm.Environment env vm.Environment
address *common.Address address *common.Address
input []byte input []byte
evm vm.VirtualMachine
Gas, price, value *big.Int Gas, price, value *big.Int
} }
func NewExecution(env vm.Environment, address *common.Address, input []byte, gas, gasPrice, value *big.Int) *Execution { func NewExecution(env vm.Environment, address *common.Address, input []byte, gas, gasPrice, value *big.Int) *Execution {
return &Execution{env: env, address: address, input: input, Gas: gas, price: gasPrice, value: value} exe := &Execution{env: env, address: address, input: input, Gas: gas, price: gasPrice, value: value}
exe.evm = vm.NewVm(env)
return exe
} }
func (self *Execution) Call(codeAddr common.Address, caller vm.ContextRef) ([]byte, error) { func (self *Execution) Call(codeAddr common.Address, caller vm.ContextRef) ([]byte, error) {
@ -28,28 +33,47 @@ func (self *Execution) Call(codeAddr common.Address, caller vm.ContextRef) ([]by
return self.exec(&codeAddr, code, caller) return self.exec(&codeAddr, code, caller)
} }
func (self *Execution) Create(caller vm.ContextRef) (ret []byte, err error, account *state.StateObject) {
ret, err = self.exec(nil, self.input, caller)
account = self.env.State().GetStateObject(*self.address)
return
}
func (self *Execution) exec(contextAddr *common.Address, code []byte, caller vm.ContextRef) (ret []byte, err error) { func (self *Execution) exec(contextAddr *common.Address, code []byte, caller vm.ContextRef) (ret []byte, err error) {
start := time.Now() start := time.Now()
env := self.env env := self.env
evm := vm.NewVm(env) evm := self.evm
if env.Depth() == vm.MaxCallDepth { if env.Depth() > int(params.CallCreateDepth.Int64()) {
caller.ReturnGas(self.Gas, self.price) caller.ReturnGas(self.Gas, self.price)
return nil, vm.DepthError{} return nil, vm.DepthError{}
} }
vsnapshot := env.State().Copy() vsnapshot := env.State().Copy()
var createAccount bool
if self.address == nil { if self.address == nil {
// Generate a new address // Generate a new address
nonce := env.State().GetNonce(caller.Address()) nonce := env.State().GetNonce(caller.Address())
addr := crypto.CreateAddress(caller.Address(), nonce)
env.State().SetNonce(caller.Address(), nonce+1) env.State().SetNonce(caller.Address(), nonce+1)
addr := crypto.CreateAddress(caller.Address(), nonce)
self.address = &addr self.address = &addr
createAccount = true
} }
snapshot := env.State().Copy() snapshot := env.State().Copy()
from, to := env.State().GetStateObject(caller.Address()), env.State().GetOrNewStateObject(*self.address) var (
from = env.State().GetStateObject(caller.Address())
to *state.StateObject
)
if createAccount {
to = env.State().CreateAccount(*self.address)
} else {
to = env.State().GetOrNewStateObject(*self.address)
}
err = env.Transfer(from, to, self.value) err = env.Transfer(from, to, self.value)
if err != nil { if err != nil {
env.State().Set(vsnapshot) env.State().Set(vsnapshot)
@ -70,10 +94,3 @@ func (self *Execution) exec(contextAddr *common.Address, code []byte, caller vm.
return return
} }
func (self *Execution) Create(caller vm.ContextRef) (ret []byte, err error, account *state.StateObject) {
ret, err = self.exec(nil, self.input, caller)
account = self.env.State().GetStateObject(*self.address)
return
}

View file

@ -4,25 +4,14 @@ import (
"math" "math"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
) )
type AccountChange struct { type AccountChange struct {
Address, StateAddress []byte Address, StateAddress []byte
} }
type FilterOptions struct {
Earliest int64
Latest int64
Address []common.Address
Topics [][]common.Hash
Skip int
Max int
}
// Filtering interface // Filtering interface
type Filter struct { type Filter struct {
eth Backend eth Backend
@ -44,18 +33,6 @@ func NewFilter(eth Backend) *Filter {
return &Filter{eth: eth} return &Filter{eth: eth}
} }
// SetOptions copies the filter options to the filter it self. The reason for this "silly" copy
// is simply because named arguments in this case is extremely nice and readable.
func (self *Filter) SetOptions(options *FilterOptions) {
self.earliest = options.Earliest
self.latest = options.Latest
self.skip = options.Skip
self.max = options.Max
self.address = options.Address
self.topics = options.Topics
}
// Set the earliest and latest block for filtering. // Set the earliest and latest block for filtering.
// -1 = latest block (i.e., the current block) // -1 = latest block (i.e., the current block)
// hash = particular hash from-to // hash = particular hash from-to

View file

@ -3,12 +3,12 @@ package core
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"math/big"
"os" "os"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
) )
/* /*
@ -18,13 +18,11 @@ import (
var ZeroHash256 = make([]byte, 32) var ZeroHash256 = make([]byte, 32)
var ZeroHash160 = make([]byte, 20) var ZeroHash160 = make([]byte, 20)
var ZeroHash512 = make([]byte, 64) var ZeroHash512 = make([]byte, 64)
var GenesisDiff = big.NewInt(131072)
var GenesisGasLimit = big.NewInt(3141592)
func GenesisBlock(db common.Database) *types.Block { func GenesisBlock(db common.Database) *types.Block {
genesis := types.NewBlock(common.Hash{}, common.Address{}, common.Hash{}, GenesisDiff, 42, "") genesis := types.NewBlock(common.Hash{}, common.Address{}, common.Hash{}, params.GenesisDifficulty, 42, "")
genesis.Header().Number = common.Big0 genesis.Header().Number = common.Big0
genesis.Header().GasLimit = GenesisGasLimit genesis.Header().GasLimit = params.GenesisGasLimit
genesis.Header().GasUsed = common.Big0 genesis.Header().GasUsed = common.Big0
genesis.Header().Time = 0 genesis.Header().Time = 0
@ -34,7 +32,10 @@ func GenesisBlock(db common.Database) *types.Block {
genesis.SetTransactions(types.Transactions{}) genesis.SetTransactions(types.Transactions{})
genesis.SetReceipts(types.Receipts{}) genesis.SetReceipts(types.Receipts{})
var accounts map[string]struct{ Balance string } var accounts map[string]struct {
Balance string
Code string
}
err := json.Unmarshal(genesisData, &accounts) err := json.Unmarshal(genesisData, &accounts)
if err != nil { if err != nil {
fmt.Println("enable to decode genesis json data:", err) fmt.Println("enable to decode genesis json data:", err)
@ -44,8 +45,9 @@ func GenesisBlock(db common.Database) *types.Block {
statedb := state.New(genesis.Root(), db) statedb := state.New(genesis.Root(), db)
for addr, account := range accounts { for addr, account := range accounts {
codedAddr := common.Hex2Bytes(addr) codedAddr := common.Hex2Bytes(addr)
accountState := statedb.GetAccount(common.BytesToAddress(codedAddr)) accountState := statedb.CreateAccount(common.BytesToAddress(codedAddr))
accountState.SetBalance(common.Big(account.Balance)) accountState.SetBalance(common.Big(account.Balance))
accountState.SetCode(common.FromHex(account.Code))
statedb.UpdateStateObject(accountState) statedb.UpdateStateObject(accountState)
} }
statedb.Sync() statedb.Sync()

View file

@ -68,11 +68,11 @@ func TestNull(t *testing.T) {
state := New(common.Hash{}, db) state := New(common.Hash{}, db)
address := common.HexToAddress("0x823140710bf13990e4500136726d8b55") address := common.HexToAddress("0x823140710bf13990e4500136726d8b55")
state.NewStateObject(address) state.CreateAccount(address)
//value := common.FromHex("0x823140710bf13990e4500136726d8b55") //value := common.FromHex("0x823140710bf13990e4500136726d8b55")
value := make([]byte, 16) value := make([]byte, 16)
state.SetState(address, common.Hash{}, value) state.SetState(address, common.Hash{}, value)
state.Update(nil) state.Update()
state.Sync() state.Sync()
value = state.GetState(address, common.Hash{}) value = state.GetState(address, common.Hash{})
} }

View file

@ -57,6 +57,10 @@ func (self *StateDB) Refund(address common.Address, gas *big.Int) {
self.refund[addr].Add(self.refund[addr], gas) self.refund[addr].Add(self.refund[addr], gas)
} }
/*
* GETTERS
*/
// Retrieve the balance from the given address or 0 if object not found // Retrieve the balance from the given address or 0 if object not found
func (self *StateDB) GetBalance(addr common.Address) *big.Int { func (self *StateDB) GetBalance(addr common.Address) *big.Int {
stateObject := self.GetStateObject(addr) stateObject := self.GetStateObject(addr)
@ -67,13 +71,6 @@ func (self *StateDB) GetBalance(addr common.Address) *big.Int {
return common.Big0 return common.Big0
} }
func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
stateObject := self.GetStateObject(addr)
if stateObject != nil {
stateObject.AddBalance(amount)
}
}
func (self *StateDB) GetNonce(addr common.Address) uint64 { func (self *StateDB) GetNonce(addr common.Address) uint64 {
stateObject := self.GetStateObject(addr) stateObject := self.GetStateObject(addr)
if stateObject != nil { if stateObject != nil {
@ -101,22 +98,41 @@ func (self *StateDB) GetState(a common.Address, b common.Hash) []byte {
return nil return nil
} }
func (self *StateDB) SetNonce(addr common.Address, nonce uint64) { func (self *StateDB) IsDeleted(addr common.Address) bool {
stateObject := self.GetStateObject(addr) stateObject := self.GetStateObject(addr)
if stateObject != nil {
return stateObject.remove
}
return false
}
/*
* SETTERS
*/
func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.AddBalance(amount)
}
}
func (self *StateDB) SetNonce(addr common.Address, nonce uint64) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil { if stateObject != nil {
stateObject.SetNonce(nonce) stateObject.SetNonce(nonce)
} }
} }
func (self *StateDB) SetCode(addr common.Address, code []byte) { func (self *StateDB) SetCode(addr common.Address, code []byte) {
stateObject := self.GetStateObject(addr) stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil { if stateObject != nil {
stateObject.SetCode(code) stateObject.SetCode(code)
} }
} }
func (self *StateDB) SetState(addr common.Address, key common.Hash, value interface{}) { func (self *StateDB) SetState(addr common.Address, key common.Hash, value interface{}) {
stateObject := self.GetStateObject(addr) stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil { if stateObject != nil {
stateObject.SetState(key, common.NewValue(value)) stateObject.SetState(key, common.NewValue(value))
} }
@ -134,14 +150,6 @@ func (self *StateDB) Delete(addr common.Address) bool {
return false return false
} }
func (self *StateDB) IsDeleted(addr common.Address) bool {
stateObject := self.GetStateObject(addr)
if stateObject != nil {
return stateObject.remove
}
return false
}
// //
// Setting, updating & deleting state object methods // Setting, updating & deleting state object methods
// //
@ -194,16 +202,14 @@ func (self *StateDB) SetStateObject(object *StateObject) {
func (self *StateDB) GetOrNewStateObject(addr common.Address) *StateObject { func (self *StateDB) GetOrNewStateObject(addr common.Address) *StateObject {
stateObject := self.GetStateObject(addr) stateObject := self.GetStateObject(addr)
if stateObject == nil { if stateObject == nil {
stateObject = self.NewStateObject(addr) stateObject = self.CreateAccount(addr)
} }
return stateObject return stateObject
} }
// Create a state object whether it exist in the trie or not // NewStateObject create a state object whether it exist in the trie or not
func (self *StateDB) NewStateObject(addr common.Address) *StateObject { func (self *StateDB) newStateObject(addr common.Address) *StateObject {
//addr = common.Address(addr)
statelogger.Debugf("(+) %x\n", addr) statelogger.Debugf("(+) %x\n", addr)
stateObject := NewStateObject(addr, self.db) stateObject := NewStateObject(addr, self.db)
@ -212,9 +218,19 @@ func (self *StateDB) NewStateObject(addr common.Address) *StateObject {
return stateObject return stateObject
} }
// Deprecated // Creates creates a new state object and takes ownership. This is different from "NewStateObject"
func (self *StateDB) GetAccount(addr common.Address) *StateObject { func (self *StateDB) CreateAccount(addr common.Address) *StateObject {
return self.GetOrNewStateObject(addr) // Get previous (if any)
so := self.GetStateObject(addr)
// Create a new one
newSo := self.newStateObject(addr)
// If it existed set the balance to the new account
if so != nil {
newSo.balance = so.balance
}
return newSo
} }
// //
@ -300,7 +316,7 @@ func (self *StateDB) Refunds() map[string]*big.Int {
return self.refund return self.refund
} }
func (self *StateDB) Update(gasUsed *big.Int) { func (self *StateDB) Update() {
self.refund = make(map[string]*big.Int) self.refund = make(map[string]*big.Int)
for _, stateObject := range self.stateObjects { for _, stateObject := range self.stateObjects {

View file

@ -8,6 +8,7 @@ import (
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
) )
const tryJit = false const tryJit = false
@ -178,20 +179,21 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er
) )
// Transaction gas // Transaction gas
if err = self.UseGas(vm.GasTx); err != nil { if err = self.UseGas(params.TxGas); err != nil {
return nil, nil, InvalidTxError(err) return nil, nil, InvalidTxError(err)
} }
// Pay data gas // Pay data gas
var dgas int64 dgas := new(big.Int)
for _, byt := range self.data { for _, byt := range self.data {
if byt != 0 { if byt != 0 {
dgas += vm.GasTxDataNonzeroByte.Int64() dgas.Add(dgas, params.TxDataNonZeroGas)
} else { } else {
dgas += vm.GasTxDataZeroByte.Int64() dgas.Add(dgas, params.TxDataZeroGas)
} }
} }
if err = self.UseGas(big.NewInt(dgas)); err != nil {
if err = self.UseGas(dgas); err != nil {
return nil, nil, InvalidTxError(err) return nil, nil, InvalidTxError(err)
} }
@ -201,7 +203,7 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er
ret, err, ref = vmenv.Create(sender, self.msg.Data(), self.gas, self.gasPrice, self.value) ret, err, ref = vmenv.Create(sender, self.msg.Data(), self.gas, self.gasPrice, self.value)
if err == nil { if err == nil {
dataGas := big.NewInt(int64(len(ret))) dataGas := big.NewInt(int64(len(ret)))
dataGas.Mul(dataGas, vm.GasCreateByte) dataGas.Mul(dataGas, params.CreateDataGas)
if err := self.UseGas(dataGas); err == nil { if err := self.UseGas(dataGas); err == nil {
ref.SetCode(ret) ref.SetCode(ret)
} else { } else {

View file

@ -148,6 +148,23 @@ func NewBlockWithHeader(header *Header) *Block {
return &Block{header: header} return &Block{header: header}
} }
func (self *Block) ValidateFields() error {
if self.header == nil {
return fmt.Errorf("header is nil")
}
for i, transaction := range self.transactions {
if transaction == nil {
return fmt.Errorf("transaction %d is nil", i)
}
}
for i, uncle := range self.uncles {
if uncle == nil {
return fmt.Errorf("uncle %d is nil", i)
}
}
return nil
}
func (self *Block) DecodeRLP(s *rlp.Stream) error { func (self *Block) DecodeRLP(s *rlp.Stream) error {
var eb extblock var eb extblock
if err := s.Decode(&eb); err != nil { if err := s.Decode(&eb); err != nil {

View file

@ -3,8 +3,9 @@ package vm
import ( import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
) )
type Address interface { type Address interface {
@ -27,28 +28,28 @@ func PrecompiledContracts() map[string]*PrecompiledAccount {
return map[string]*PrecompiledAccount{ return map[string]*PrecompiledAccount{
// ECRECOVER // ECRECOVER
string(common.LeftPadBytes([]byte{1}, 20)): &PrecompiledAccount{func(l int) *big.Int { string(common.LeftPadBytes([]byte{1}, 20)): &PrecompiledAccount{func(l int) *big.Int {
return GasEcrecover return params.EcrecoverGas
}, ecrecoverFunc}, }, ecrecoverFunc},
// SHA256 // SHA256
string(common.LeftPadBytes([]byte{2}, 20)): &PrecompiledAccount{func(l int) *big.Int { string(common.LeftPadBytes([]byte{2}, 20)): &PrecompiledAccount{func(l int) *big.Int {
n := big.NewInt(int64(l+31) / 32) n := big.NewInt(int64(l+31) / 32)
n.Mul(n, GasSha256Word) n.Mul(n, params.Sha256WordGas)
return n.Add(n, GasSha256Base) return n.Add(n, params.Sha256Gas)
}, sha256Func}, }, sha256Func},
// RIPEMD160 // RIPEMD160
string(common.LeftPadBytes([]byte{3}, 20)): &PrecompiledAccount{func(l int) *big.Int { string(common.LeftPadBytes([]byte{3}, 20)): &PrecompiledAccount{func(l int) *big.Int {
n := big.NewInt(int64(l+31) / 32) n := big.NewInt(int64(l+31) / 32)
n.Mul(n, GasRipemdWord) n.Mul(n, params.Ripemd160WordGas)
return n.Add(n, GasRipemdBase) return n.Add(n, params.Ripemd160Gas)
}, ripemd160Func}, }, ripemd160Func},
string(common.LeftPadBytes([]byte{4}, 20)): &PrecompiledAccount{func(l int) *big.Int { string(common.LeftPadBytes([]byte{4}, 20)): &PrecompiledAccount{func(l int) *big.Int {
n := big.NewInt(int64(l+31) / 32) n := big.NewInt(int64(l+31) / 32)
n.Mul(n, GasIdentityWord) n.Mul(n, params.IdentityWordGas)
return n.Add(n, GasIdentityBase) return n.Add(n, params.IdentityGas)
}, memCpy}, }, memCpy},
} }
} }
@ -61,15 +62,32 @@ func ripemd160Func(in []byte) []byte {
return common.LeftPadBytes(crypto.Ripemd160(in), 32) return common.LeftPadBytes(crypto.Ripemd160(in), 32)
} }
const ecRecoverInputLength = 128
func ecrecoverFunc(in []byte) []byte { func ecrecoverFunc(in []byte) []byte {
// In case of an invalid sig. Defaults to return nil // "in" is (hash, v, r, s), each 32 bytes
defer func() { recover() }() // but for ecrecover we want (r, s, v)
if len(in) < ecRecoverInputLength {
return nil
}
hash := in[:32] // Treat V as a 256bit integer
v := common.BigD(in[32:64]).Bytes()[0] - 27 v := new(big.Int).Sub(common.Bytes2Big(in[32:64]), big.NewInt(27))
sig := append(in[64:], v) // Ethereum requires V to be either 0 or 1 => (27 || 28)
if !(v.Cmp(Zero) == 0 || v.Cmp(One) == 0) {
return nil
}
return common.LeftPadBytes(crypto.Sha3(crypto.Ecrecover(append(hash, sig...))[1:])[12:], 32) // v needs to be moved to the end
rsv := append(in[64:128], byte(v.Uint64()))
pubKey := crypto.Ecrecover(in[:32], rsv)
// make sure the public key is a valid one
if pubKey == nil || len(pubKey) != 65 {
return nil
}
// the first byte of pubkey is bitcoin heritage
return common.LeftPadBytes(crypto.Sha3(pubKey[1:])[12:], 32)
} }
func memCpy(in []byte) []byte { func memCpy(in []byte) []byte {

View file

@ -1,9 +1,25 @@
package vm package vm
import "gopkg.in/fatih/set.v0" import (
"math/big"
func analyseJumpDests(code []byte) (dests *set.Set) { "gopkg.in/fatih/set.v0"
dests = set.New() )
type destinations struct {
set *set.Set
}
func (d *destinations) Has(dest *big.Int) bool {
return d.set.Has(string(dest.Bytes()))
}
func (d *destinations) Add(dest *big.Int) {
d.set.Add(string(dest.Bytes()))
}
func analyseJumpDests(code []byte) (dests *destinations) {
dests = &destinations{set.New()}
for pc := uint64(0); pc < uint64(len(code)); pc++ { for pc := uint64(0); pc < uint64(len(code)); pc++ {
var op OpCode = OpCode(code[pc]) var op OpCode = OpCode(code[pc])
@ -13,7 +29,7 @@ func analyseJumpDests(code []byte) (dests *set.Set) {
pc += a pc += a
case JUMPDEST: case JUMPDEST:
dests.Add(pc) dests.Add(big.NewInt(int64(pc)))
} }
} }
return return

View file

@ -20,8 +20,6 @@ const (
JitVmTy JitVmTy
MaxVmTy MaxVmTy
MaxCallDepth = 1025
LogTyPretty byte = 0x1 LogTyPretty byte = 0x1
LogTyDiff byte = 0x2 LogTyDiff byte = 0x2
) )
@ -33,6 +31,7 @@ var (
S256 = common.S256 S256 = common.S256
Zero = common.Big0 Zero = common.Big0
One = common.Big1
max = big.NewInt(math.MaxInt64) max = big.NewInt(math.MaxInt64)
) )
@ -80,3 +79,13 @@ func getData(data []byte, start, size *big.Int) []byte {
e := common.BigMin(new(big.Int).Add(s, size), dlen) e := common.BigMin(new(big.Int).Add(s, size), dlen)
return common.RightPadBytes(data[s.Uint64():e.Uint64()], int(size.Uint64())) return common.RightPadBytes(data[s.Uint64():e.Uint64()], int(size.Uint64()))
} }
func UseGas(gas, amount *big.Int) bool {
if gas.Cmp(amount) < 0 {
return false
}
// Sub the amount of gas from the remaining
gas.Sub(gas, amount)
return true
}

View file

@ -1,7 +1,6 @@
package vm package vm
import ( import (
"math"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -41,29 +40,18 @@ func NewContext(caller ContextRef, object ContextRef, value, gas, price *big.Int
return c return c
} }
func (c *Context) GetOp(n uint64) OpCode { func (c *Context) GetOp(n *big.Int) OpCode {
return OpCode(c.GetByte(n)) return OpCode(c.GetByte(n))
} }
func (c *Context) GetByte(n uint64) byte { func (c *Context) GetByte(n *big.Int) byte {
if n < uint64(len(c.Code)) { if n.Cmp(big.NewInt(int64(len(c.Code)))) < 0 {
return c.Code[n] return c.Code[n.Int64()]
} }
return 0 return 0
} }
func (c *Context) GetBytes(x, y int) []byte {
return c.GetRangeValue(uint64(x), uint64(y))
}
func (c *Context) GetRangeValue(x, size uint64) []byte {
x = uint64(math.Min(float64(x), float64(len(c.Code))))
y := uint64(math.Min(float64(x+size), float64(len(c.Code))))
return common.RightPadBytes(c.Code[x:y], int(size))
}
func (c *Context) Return(ret []byte) []byte { func (c *Context) Return(ret []byte) []byte {
// Return the remaining gas to the caller // Return the remaining gas to the caller
c.caller.ReturnGas(c.Gas, c.Price) c.caller.ReturnGas(c.Gas, c.Price)
@ -74,16 +62,12 @@ func (c *Context) Return(ret []byte) []byte {
/* /*
* Gas functions * Gas functions
*/ */
func (c *Context) UseGas(gas *big.Int) bool { func (c *Context) UseGas(gas *big.Int) (ok bool) {
if c.Gas.Cmp(gas) < 0 { ok = UseGas(c.Gas, gas)
return false if ok {
}
// Sub the amount of gas from the remaining
c.Gas.Sub(c.Gas, gas)
c.UsedGas.Add(c.UsedGas, gas) c.UsedGas.Add(c.UsedGas, gas)
}
return true return
} }
// Implement the caller interface // Implement the caller interface

View file

@ -2,6 +2,7 @@ package vm
import ( import (
"fmt" "fmt"
"github.com/ethereum/go-ethereum/params"
"math/big" "math/big"
) )
@ -42,7 +43,7 @@ func IsStack(err error) bool {
type DepthError struct{} type DepthError struct{}
func (self DepthError) Error() string { func (self DepthError) Error() string {
return fmt.Sprintf("Max call depth exceeded (%d)", MaxCallDepth) return fmt.Sprintf("Max call depth exceeded (%d)", params.CallCreateDepth)
} }
func IsDepthErr(err error) bool { func IsDepthErr(err error) bool {

View file

@ -1,11 +1,10 @@
package vm package vm
import "math/big" import (
"fmt"
type req struct { "github.com/ethereum/go-ethereum/params"
stack int "math/big"
gas *big.Int )
}
var ( var (
GasQuickStep = big.NewInt(2) GasQuickStep = big.NewInt(2)
@ -15,116 +14,36 @@ var (
GasSlowStep = big.NewInt(10) GasSlowStep = big.NewInt(10)
GasExtStep = big.NewInt(20) GasExtStep = big.NewInt(20)
GasStorageGet = big.NewInt(50)
GasStorageAdd = big.NewInt(20000)
GasStorageMod = big.NewInt(5000)
GasLogBase = big.NewInt(375)
GasLogTopic = big.NewInt(375)
GasLogByte = big.NewInt(8)
GasCreate = big.NewInt(32000)
GasCreateByte = big.NewInt(200)
GasCall = big.NewInt(40)
GasCallValueTransfer = big.NewInt(9000)
GasStipend = big.NewInt(2300)
GasCallNewAccount = big.NewInt(25000)
GasReturn = big.NewInt(0) GasReturn = big.NewInt(0)
GasStop = big.NewInt(0) GasStop = big.NewInt(0)
GasJumpDest = big.NewInt(1)
RefundStorage = big.NewInt(15000)
RefundSuicide = big.NewInt(24000)
GasMemWord = big.NewInt(3)
GasQuadCoeffDenom = big.NewInt(512)
GasContractByte = big.NewInt(200) GasContractByte = big.NewInt(200)
GasTransaction = big.NewInt(21000)
GasTxDataNonzeroByte = big.NewInt(68)
GasTxDataZeroByte = big.NewInt(4)
GasTx = big.NewInt(21000)
GasExp = big.NewInt(10)
GasExpByte = big.NewInt(10)
GasSha3Base = big.NewInt(30)
GasSha3Word = big.NewInt(6)
GasSha256Base = big.NewInt(60)
GasSha256Word = big.NewInt(12)
GasRipemdBase = big.NewInt(600)
GasRipemdWord = big.NewInt(12)
GasEcrecover = big.NewInt(3000)
GasIdentityBase = big.NewInt(15)
GasIdentityWord = big.NewInt(3)
GasCopyWord = big.NewInt(3)
) )
var _baseCheck = map[OpCode]req{ func baseCheck(op OpCode, stack *stack, gas *big.Int) error {
// Req stack Gas price // PUSH and DUP are a bit special. They all cost the same but we do want to have checking on stack push limit
ADD: {2, GasFastestStep}, // PUSH is also allowed to calculate the same price for all PUSHes
LT: {2, GasFastestStep}, // DUP requirements are handled elsewhere (except for the stack limit check)
GT: {2, GasFastestStep}, if op >= PUSH1 && op <= PUSH32 {
SLT: {2, GasFastestStep}, op = PUSH1
SGT: {2, GasFastestStep}, }
EQ: {2, GasFastestStep}, if op >= DUP1 && op <= DUP16 {
ISZERO: {1, GasFastestStep}, op = DUP1
SUB: {2, GasFastestStep}, }
AND: {2, GasFastestStep},
OR: {2, GasFastestStep},
XOR: {2, GasFastestStep},
NOT: {1, GasFastestStep},
BYTE: {2, GasFastestStep},
CALLDATALOAD: {1, GasFastestStep},
CALLDATACOPY: {3, GasFastestStep},
MLOAD: {1, GasFastestStep},
MSTORE: {2, GasFastestStep},
MSTORE8: {2, GasFastestStep},
CODECOPY: {3, GasFastestStep},
MUL: {2, GasFastStep},
DIV: {2, GasFastStep},
SDIV: {2, GasFastStep},
MOD: {2, GasFastStep},
SMOD: {2, GasFastStep},
SIGNEXTEND: {2, GasFastStep},
ADDMOD: {3, GasMidStep},
MULMOD: {3, GasMidStep},
JUMP: {1, GasMidStep},
JUMPI: {2, GasSlowStep},
EXP: {2, GasSlowStep},
ADDRESS: {0, GasQuickStep},
ORIGIN: {0, GasQuickStep},
CALLER: {0, GasQuickStep},
CALLVALUE: {0, GasQuickStep},
CODESIZE: {0, GasQuickStep},
GASPRICE: {0, GasQuickStep},
COINBASE: {0, GasQuickStep},
TIMESTAMP: {0, GasQuickStep},
NUMBER: {0, GasQuickStep},
CALLDATASIZE: {0, GasQuickStep},
DIFFICULTY: {0, GasQuickStep},
GASLIMIT: {0, GasQuickStep},
POP: {0, GasQuickStep},
PC: {0, GasQuickStep},
MSIZE: {0, GasQuickStep},
GAS: {0, GasQuickStep},
BLOCKHASH: {1, GasExtStep},
BALANCE: {0, GasExtStep},
EXTCODESIZE: {1, GasExtStep},
EXTCODECOPY: {4, GasExtStep},
SLOAD: {1, GasStorageGet},
SSTORE: {2, Zero},
SHA3: {1, GasSha3Base},
CREATE: {3, GasCreate},
CALL: {7, GasCall},
CALLCODE: {7, GasCall},
JUMPDEST: {0, GasJumpDest},
SUICIDE: {1, Zero},
RETURN: {2, Zero},
}
func baseCheck(op OpCode, stack *stack, gas *big.Int) {
if r, ok := _baseCheck[op]; ok { if r, ok := _baseCheck[op]; ok {
stack.require(r.stack) err := stack.require(r.stackPop)
if err != nil {
return err
}
if r.stackPush && len(stack.data)-r.stackPop+1 > int(params.StackLimit.Int64()) {
return fmt.Errorf("stack limit reached (%d)", params.StackLimit.Int64())
}
gas.Add(gas, r.gas) gas.Add(gas, r.gas)
} }
return nil
} }
func toWordSize(size *big.Int) *big.Int { func toWordSize(size *big.Int) *big.Int {
@ -133,3 +52,74 @@ func toWordSize(size *big.Int) *big.Int {
tmp.Div(tmp, u256(32)) tmp.Div(tmp, u256(32))
return tmp return tmp
} }
type req struct {
stackPop int
gas *big.Int
stackPush bool
}
var _baseCheck = map[OpCode]req{
// opcode | stack pop | gas price | stack push
ADD: {2, GasFastestStep, true},
LT: {2, GasFastestStep, true},
GT: {2, GasFastestStep, true},
SLT: {2, GasFastestStep, true},
SGT: {2, GasFastestStep, true},
EQ: {2, GasFastestStep, true},
ISZERO: {1, GasFastestStep, true},
SUB: {2, GasFastestStep, true},
AND: {2, GasFastestStep, true},
OR: {2, GasFastestStep, true},
XOR: {2, GasFastestStep, true},
NOT: {1, GasFastestStep, true},
BYTE: {2, GasFastestStep, true},
CALLDATALOAD: {1, GasFastestStep, true},
CALLDATACOPY: {3, GasFastestStep, true},
MLOAD: {1, GasFastestStep, true},
MSTORE: {2, GasFastestStep, false},
MSTORE8: {2, GasFastestStep, false},
CODECOPY: {3, GasFastestStep, false},
MUL: {2, GasFastStep, true},
DIV: {2, GasFastStep, true},
SDIV: {2, GasFastStep, true},
MOD: {2, GasFastStep, true},
SMOD: {2, GasFastStep, true},
SIGNEXTEND: {2, GasFastStep, true},
ADDMOD: {3, GasMidStep, true},
MULMOD: {3, GasMidStep, true},
JUMP: {1, GasMidStep, false},
JUMPI: {2, GasSlowStep, false},
EXP: {2, GasSlowStep, true},
ADDRESS: {0, GasQuickStep, true},
ORIGIN: {0, GasQuickStep, true},
CALLER: {0, GasQuickStep, true},
CALLVALUE: {0, GasQuickStep, true},
CODESIZE: {0, GasQuickStep, true},
GASPRICE: {0, GasQuickStep, true},
COINBASE: {0, GasQuickStep, true},
TIMESTAMP: {0, GasQuickStep, true},
NUMBER: {0, GasQuickStep, true},
CALLDATASIZE: {0, GasQuickStep, true},
DIFFICULTY: {0, GasQuickStep, true},
GASLIMIT: {0, GasQuickStep, true},
POP: {1, GasQuickStep, false},
PC: {0, GasQuickStep, true},
MSIZE: {0, GasQuickStep, true},
GAS: {0, GasQuickStep, true},
BLOCKHASH: {1, GasExtStep, true},
BALANCE: {1, GasExtStep, true},
EXTCODESIZE: {1, GasExtStep, true},
EXTCODECOPY: {4, GasExtStep, false},
SLOAD: {1, params.SloadGas, true},
SSTORE: {2, Zero, false},
SHA3: {2, params.Sha3Gas, true},
CREATE: {3, params.CreateGas, true},
CALL: {7, params.CallGas, true},
CALLCODE: {7, params.CallGas, true},
JUMPDEST: {0, params.JumpdestGas, false},
SUICIDE: {1, Zero, false},
RETURN: {2, Zero, false},
PUSH1: {0, GasFastestStep, true},
DUP1: {0, Zero, true},
}

View file

@ -15,21 +15,17 @@ func NewMemory() *Memory {
} }
func (m *Memory) Set(offset, size uint64, value []byte) { func (m *Memory) Set(offset, size uint64, value []byte) {
value = common.RightPadBytes(value, int(size)) // length of store may never be less than offset + size.
// The store should be resized PRIOR to setting the memory
if size > uint64(len(m.store)) {
panic("INVALID memory: store empty")
}
totSize := offset + size // It's possible the offset is greater than 0 and size equals 0. This is because
lenSize := uint64(len(m.store) - 1) // the calcMemSize (common.go) could potentially return 0 when size is zero (NO-OP)
if totSize > lenSize { if size > 0 {
// Calculate the diff between the sizes copy(m.store[offset:offset+size], common.RightPadBytes(value, int(size)))
diff := totSize - lenSize
if diff > 0 {
// Create a new empty slice and append it
newSlice := make([]byte, diff-1)
// Resize slice
m.store = append(m.store, newSlice...)
} }
}
copy(m.store[offset:offset+size], value)
} }
func (m *Memory) Resize(size uint64) { func (m *Memory) Resize(size uint64) {

View file

@ -15,6 +15,7 @@ type stack struct {
} }
func (st *stack) push(d *big.Int) { func (st *stack) push(d *big.Int) {
// NOTE push limit (1024) is checked in baseCheck
stackItem := new(big.Int).Set(d) stackItem := new(big.Int).Set(d)
if len(st.data) > st.ptr { if len(st.data) > st.ptr {
st.data[st.ptr] = stackItem st.data[st.ptr] = stackItem
@ -46,10 +47,11 @@ func (st *stack) peek() *big.Int {
return st.data[st.len()-1] return st.data[st.len()-1]
} }
func (st *stack) require(n int) { func (st *stack) require(n int) error {
if st.len() < n { if st.len() < n {
panic(fmt.Sprintf("stack underflow (%d <=> %d)", len(st.data), n)) return fmt.Errorf("stack underflow (%d <=> %d)", len(st.data), n)
} }
return nil
} }
func (st *stack) Print() { func (st *stack) Print() {

View file

@ -7,6 +7,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
) )
type Vm struct { type Vm struct {
@ -24,6 +25,9 @@ type Vm struct {
Fn string Fn string
Recoverable bool Recoverable bool
// Will be called before the vm returns
After func(*Context, error)
} }
func New(env Environment) *Vm { func New(env Environment) *Vm {
@ -45,20 +49,20 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
self.Printf("(%d) (%x) %x (code=%d) gas: %v (d) %x", self.env.Depth(), caller.Address().Bytes()[:4], context.Address(), len(code), context.Gas, callData).Endl() self.Printf("(%d) (%x) %x (code=%d) gas: %v (d) %x", self.env.Depth(), caller.Address().Bytes()[:4], context.Address(), len(code), context.Gas, callData).Endl()
if self.Recoverable { // User defer pattern to check for an error and, based on the error being nil or not, use all gas and return.
// Recover from any require exception
defer func() { defer func() {
if r := recover(); r != nil { if self.After != nil {
self.Printf(" %v", r).Endl() self.After(context, err)
}
if err != nil {
self.Printf(" %v", err).Endl()
// In case of a VM exception (known exceptions) all gas consumed (panics NOT included).
context.UseGas(context.Gas) context.UseGas(context.Gas)
ret = context.Return(nil) ret = context.Return(nil)
err = fmt.Errorf("%v", r)
} }
}() }()
}
if context.CodeAddr != nil { if context.CodeAddr != nil {
if p := Precompiled[context.CodeAddr.Str()]; p != nil { if p := Precompiled[context.CodeAddr.Str()]; p != nil {
@ -72,22 +76,21 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
destinations = analyseJumpDests(context.Code) destinations = analyseJumpDests(context.Code)
mem = NewMemory() mem = NewMemory()
stack = newStack() stack = newStack()
pc uint64 = 0 pc = new(big.Int)
step = 0
statedb = self.env.State() statedb = self.env.State()
jump = func(from uint64, to *big.Int) { jump = func(from *big.Int, to *big.Int) error {
p := to.Uint64() nop := context.GetOp(to)
if !destinations.Has(to) {
nop := context.GetOp(p) return fmt.Errorf("invalid jump destination (%v) %v", nop, to)
if !destinations.Has(p) {
panic(fmt.Sprintf("invalid jump destination (%v) %v", nop, p))
} }
self.Printf(" ~> %v", to) self.Printf(" ~> %v", to)
pc = to.Uint64() pc = to
self.Endl() self.Endl()
return nil
} }
) )
@ -100,12 +103,14 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
// The base for all big integer arithmetic // The base for all big integer arithmetic
base := new(big.Int) base := new(big.Int)
step++
// Get the memory location of pc // Get the memory location of pc
op = context.GetOp(pc) op = context.GetOp(pc)
self.Printf("(pc) %-3d -o- %-14s (m) %-4d (s) %-4d ", pc, op.String(), mem.Len(), stack.len()) self.Printf("(pc) %-3d -o- %-14s (m) %-4d (s) %-4d ", pc, op.String(), mem.Len(), stack.len())
newMemSize, gas := self.calculateGasAndSize(context, caller, op, statedb, mem, stack) newMemSize, gas, err := self.calculateGasAndSize(context, caller, op, statedb, mem, stack)
if err != nil {
return nil, err
}
self.Printf("(g) %-3v (%v)", gas, context.Gas) self.Printf("(g) %-3v (%v)", gas, context.Gas)
@ -420,22 +425,11 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
self.Printf(" => %v", value) self.Printf(" => %v", value)
case CALLDATALOAD: case CALLDATALOAD:
var ( data := getData(callData, stack.pop(), common.Big32)
offset = stack.pop()
data = make([]byte, 32)
lenData = big.NewInt(int64(len(callData)))
)
if lenData.Cmp(offset) >= 0 {
length := new(big.Int).Add(offset, common.Big32)
length = common.BigMin(length, lenData)
copy(data, callData[offset.Int64():length.Int64()])
}
self.Printf(" => 0x%x", data) self.Printf(" => 0x%x", data)
stack.push(common.BigD(data)) stack.push(common.Bytes2Big(data))
case CALLDATASIZE: case CALLDATASIZE:
l := int64(len(callData)) l := int64(len(callData))
stack.push(big.NewInt(l)) stack.push(big.NewInt(l))
@ -534,13 +528,11 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
// 0x50 range // 0x50 range
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
a := uint64(op - PUSH1 + 1) a := big.NewInt(int64(op - PUSH1 + 1))
byts := context.GetRangeValue(pc+1, a) byts := getData(code, new(big.Int).Add(pc, big.NewInt(1)), a)
// push value to stack // push value to stack
stack.push(common.BigD(byts)) stack.push(common.Bytes2Big(byts))
pc += a pc.Add(pc, a)
step += int(op) - int(PUSH1) + 1
self.Printf(" => 0x%x", byts) self.Printf(" => 0x%x", byts)
case POP: case POP:
@ -600,14 +592,18 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
self.Printf(" {0x%x : 0x%x}", loc, val.Bytes()) self.Printf(" {0x%x : 0x%x}", loc, val.Bytes())
case JUMP: case JUMP:
jump(pc, stack.pop()) if err := jump(pc, stack.pop()); err != nil {
return nil, err
}
continue continue
case JUMPI: case JUMPI:
pos, cond := stack.pop(), stack.pop() pos, cond := stack.pop(), stack.pop()
if cond.Cmp(common.BigTrue) >= 0 { if cond.Cmp(common.BigTrue) >= 0 {
jump(pc, pos) if err := jump(pc, pos); err != nil {
return nil, err
}
continue continue
} }
@ -616,7 +612,8 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
case JUMPDEST: case JUMPDEST:
case PC: case PC:
stack.push(big.NewInt(int64(pc))) //stack.push(big.NewInt(int64(pc)))
stack.push(pc)
case MSIZE: case MSIZE:
stack.push(big.NewInt(int64(mem.Len()))) stack.push(big.NewInt(int64(mem.Len())))
case GAS: case GAS:
@ -642,10 +639,9 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
self.Printf(" (*) 0x0 %v", suberr) self.Printf(" (*) 0x0 %v", suberr)
} else { } else {
// gas < len(ret) * CreateDataGas == NO_CODE // gas < len(ret) * CreateDataGas == NO_CODE
dataGas := big.NewInt(int64(len(ret))) dataGas := big.NewInt(int64(len(ret)))
dataGas.Mul(dataGas, GasCreateByte) dataGas.Mul(dataGas, params.CreateDataGas)
if context.UseGas(dataGas) { if context.UseGas(dataGas) {
ref.SetCode(ret) ref.SetCode(ret)
} }
@ -672,7 +668,7 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
args := mem.Get(inOffset.Int64(), inSize.Int64()) args := mem.Get(inOffset.Int64(), inSize.Int64())
if len(value.Bytes()) > 0 { if len(value.Bytes()) > 0 {
gas.Add(gas, GasStipend) gas.Add(gas, params.CallStipend)
} }
var ( var (
@ -720,68 +716,81 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) {
default: default:
self.Printf("(pc) %-3v Invalid opcode %x\n", pc, op).Endl() self.Printf("(pc) %-3v Invalid opcode %x\n", pc, op).Endl()
panic(fmt.Errorf("Invalid opcode %x", op)) return nil, fmt.Errorf("Invalid opcode %x", op)
} }
pc++ pc.Add(pc, One)
self.Endl() self.Endl()
} }
} }
func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int) { func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCode, statedb *state.StateDB, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
var ( var (
gas = new(big.Int) gas = new(big.Int)
newMemSize *big.Int = new(big.Int) newMemSize *big.Int = new(big.Int)
) )
baseCheck(op, stack, gas) err := baseCheck(op, stack, gas)
if err != nil {
return nil, nil, err
}
// stack Check, memory resize & gas phase // stack Check, memory resize & gas phase
switch op { switch op {
case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32:
gas.Set(GasFastestStep)
case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16: case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
n := int(op - SWAP1 + 2) n := int(op - SWAP1 + 2)
stack.require(n) err := stack.require(n)
if err != nil {
return nil, nil, err
}
gas.Set(GasFastestStep) gas.Set(GasFastestStep)
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16: case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
n := int(op - DUP1 + 1) n := int(op - DUP1 + 1)
stack.require(n) err := stack.require(n)
if err != nil {
return nil, nil, err
}
gas.Set(GasFastestStep) gas.Set(GasFastestStep)
case LOG0, LOG1, LOG2, LOG3, LOG4: case LOG0, LOG1, LOG2, LOG3, LOG4:
n := int(op - LOG0) n := int(op - LOG0)
stack.require(n + 2) err := stack.require(n + 2)
if err != nil {
return nil, nil, err
}
mSize, mStart := stack.data[stack.len()-2], stack.data[stack.len()-1] mSize, mStart := stack.data[stack.len()-2], stack.data[stack.len()-1]
gas.Add(gas, GasLogBase) gas.Add(gas, params.LogGas)
gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(n)), GasLogTopic)) gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(n)), params.LogTopicGas))
gas.Add(gas, new(big.Int).Mul(mSize, GasLogByte)) gas.Add(gas, new(big.Int).Mul(mSize, params.LogDataGas))
newMemSize = calcMemSize(mStart, mSize) newMemSize = calcMemSize(mStart, mSize)
case EXP: case EXP:
gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), GasExpByte)) gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas))
case SSTORE: case SSTORE:
stack.require(2) err := stack.require(2)
if err != nil {
return nil, nil, err
}
var g *big.Int var g *big.Int
y, x := stack.data[stack.len()-2], stack.data[stack.len()-1] y, x := stack.data[stack.len()-2], stack.data[stack.len()-1]
val := statedb.GetState(context.Address(), common.BigToHash(x)) val := statedb.GetState(context.Address(), common.BigToHash(x))
if len(val) == 0 && len(y.Bytes()) > 0 { if len(val) == 0 && len(y.Bytes()) > 0 {
// 0 => non 0 // 0 => non 0
g = GasStorageAdd g = params.SstoreSetGas
} else if len(val) > 0 && len(y.Bytes()) == 0 { } else if len(val) > 0 && len(y.Bytes()) == 0 {
statedb.Refund(self.env.Origin(), RefundStorage) statedb.Refund(self.env.Origin(), params.SstoreRefundGas)
g = GasStorageMod g = params.SstoreClearGas
} else { } else {
// non 0 => non 0 (or 0 => 0) // non 0 => non 0 (or 0 => 0)
g = GasStorageMod g = params.SstoreClearGas
} }
gas.Set(g) gas.Set(g)
case SUICIDE: case SUICIDE:
if !statedb.IsDeleted(context.Address()) { if !statedb.IsDeleted(context.Address()) {
statedb.Refund(self.env.Origin(), RefundSuicide) statedb.Refund(self.env.Origin(), params.SuicideRefundGas)
} }
case MLOAD: case MLOAD:
newMemSize = calcMemSize(stack.peek(), u256(32)) newMemSize = calcMemSize(stack.peek(), u256(32))
@ -795,22 +804,22 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2]) newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
words := toWordSize(stack.data[stack.len()-2]) words := toWordSize(stack.data[stack.len()-2])
gas.Add(gas, words.Mul(words, GasSha3Word)) gas.Add(gas, words.Mul(words, params.Sha3WordGas))
case CALLDATACOPY: case CALLDATACOPY:
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3]) newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
words := toWordSize(stack.data[stack.len()-3]) words := toWordSize(stack.data[stack.len()-3])
gas.Add(gas, words.Mul(words, GasCopyWord)) gas.Add(gas, words.Mul(words, params.CopyGas))
case CODECOPY: case CODECOPY:
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3]) newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
words := toWordSize(stack.data[stack.len()-3]) words := toWordSize(stack.data[stack.len()-3])
gas.Add(gas, words.Mul(words, GasCopyWord)) gas.Add(gas, words.Mul(words, params.CopyGas))
case EXTCODECOPY: case EXTCODECOPY:
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4]) newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4])
words := toWordSize(stack.data[stack.len()-4]) words := toWordSize(stack.data[stack.len()-4])
gas.Add(gas, words.Mul(words, GasCopyWord)) gas.Add(gas, words.Mul(words, params.CopyGas))
case CREATE: case CREATE:
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3]) newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3])
@ -819,12 +828,12 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo
if op == CALL { if op == CALL {
if self.env.State().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil { if self.env.State().GetStateObject(common.BigToAddress(stack.data[stack.len()-2])) == nil {
gas.Add(gas, GasCallNewAccount) gas.Add(gas, params.CallNewAccountGas)
} }
} }
if len(stack.data[stack.len()-3].Bytes()) > 0 { if len(stack.data[stack.len()-3].Bytes()) > 0 {
gas.Add(gas, GasCallValueTransfer) gas.Add(gas, params.CallValueTransferGas)
} }
x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7]) x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7])
@ -840,20 +849,21 @@ func (self *Vm) calculateGasAndSize(context *Context, caller ContextRef, op OpCo
if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 { if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 {
oldSize := toWordSize(big.NewInt(int64(mem.Len()))) oldSize := toWordSize(big.NewInt(int64(mem.Len())))
pow := new(big.Int).Exp(oldSize, common.Big2, Zero) pow := new(big.Int).Exp(oldSize, common.Big2, Zero)
linCoef := new(big.Int).Mul(oldSize, GasMemWord) linCoef := new(big.Int).Mul(oldSize, params.MemoryGas)
quadCoef := new(big.Int).Div(pow, GasQuadCoeffDenom) quadCoef := new(big.Int).Div(pow, params.QuadCoeffDiv)
oldTotalFee := new(big.Int).Add(linCoef, quadCoef) oldTotalFee := new(big.Int).Add(linCoef, quadCoef)
pow.Exp(newMemSizeWords, common.Big2, Zero) pow.Exp(newMemSizeWords, common.Big2, Zero)
linCoef = new(big.Int).Mul(newMemSizeWords, GasMemWord) linCoef = new(big.Int).Mul(newMemSizeWords, params.MemoryGas)
quadCoef = new(big.Int).Div(pow, GasQuadCoeffDenom) quadCoef = new(big.Int).Div(pow, params.QuadCoeffDiv)
newTotalFee := new(big.Int).Add(linCoef, quadCoef) newTotalFee := new(big.Int).Add(linCoef, quadCoef)
gas.Add(gas, new(big.Int).Sub(newTotalFee, oldTotalFee)) fee := new(big.Int).Sub(newTotalFee, oldTotalFee)
gas.Add(gas, fee)
} }
} }
return newMemSize, gas return newMemSize, gas, nil
} }
func (self *Vm) RunPrecompiled(p *PrecompiledAccount, callData []byte, context *Context) (ret []byte, err error) { func (self *Vm) RunPrecompiled(p *PrecompiledAccount, callData []byte, context *Context) (ret []byte, err error) {
@ -869,7 +879,7 @@ func (self *Vm) RunPrecompiled(p *PrecompiledAccount, callData []byte, context *
tmp := new(big.Int).Set(context.Gas) tmp := new(big.Int).Set(context.Gas)
panic(OOG(gas, tmp).Error()) return nil, OOG(gas, tmp)
} }
} }

View file

@ -18,8 +18,8 @@ import (
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/crypto"
"math/big" "math/big"
"unsafe" "unsafe"
) )
@ -330,7 +330,7 @@ func env_create(_vm unsafe.Pointer, _gas *int64, _value unsafe.Pointer, initData
ret, suberr, ref := vm.env.Create(vm.me, nil, initData, gas, vm.price, value) ret, suberr, ref := vm.env.Create(vm.me, nil, initData, gas, vm.price, value)
if suberr == nil { if suberr == nil {
dataGas := big.NewInt(int64(len(ret))) // TODO: Nto the best design. env.Create can do it, it has the reference to gas counter dataGas := big.NewInt(int64(len(ret))) // TODO: Nto the best design. env.Create can do it, it has the reference to gas counter
dataGas.Mul(dataGas, GasCreateByte) dataGas.Mul(dataGas, params.CreateDataGas)
gas.Sub(gas, dataGas) gas.Sub(gas, dataGas)
*result = hash2llvm(ref.Address()) *result = hash2llvm(ref.Address())
} }

View file

@ -54,21 +54,17 @@ func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error {
return vm.Transfer(from, to, amount) return vm.Transfer(from, to, amount)
} }
func (self *VMEnv) vm(addr *common.Address, data []byte, gas, price, value *big.Int) *Execution {
return NewExecution(self, addr, data, gas, price, value)
}
func (self *VMEnv) Call(me vm.ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { func (self *VMEnv) Call(me vm.ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
exe := self.vm(&addr, data, gas, price, value) exe := NewExecution(self, &addr, data, gas, price, value)
return exe.Call(addr, me) return exe.Call(addr, me)
} }
func (self *VMEnv) CallCode(me vm.ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { func (self *VMEnv) CallCode(me vm.ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
maddr := me.Address() maddr := me.Address()
exe := self.vm(&maddr, data, gas, price, value) exe := NewExecution(self, &maddr, data, gas, price, value)
return exe.Call(addr, me) return exe.Call(addr, me)
} }
func (self *VMEnv) Create(me vm.ContextRef, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ContextRef) { func (self *VMEnv) Create(me vm.ContextRef, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ContextRef) {
exe := self.vm(nil, data, gas, price, value) exe := NewExecution(self, nil, data, gas, price, value)
return exe.Create(me) return exe.Create(me)
} }

View file

@ -9,6 +9,7 @@ import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"encoding/hex" "encoding/hex"
@ -67,13 +68,8 @@ func Ripemd160(data []byte) []byte {
return ripemd.Sum(nil) return ripemd.Sum(nil)
} }
func Ecrecover(data []byte) []byte { func Ecrecover(hash, sig []byte) []byte {
var in = struct { r, _ := secp256k1.RecoverPubkey(hash, sig)
hash []byte
sig []byte
}{data[:32], data[32:]}
r, _ := secp256k1.RecoverPubkey(in.hash, in.sig)
return r return r
} }
@ -139,14 +135,23 @@ func LoadECDSA(file string) (*ecdsa.PrivateKey, error) {
return ToECDSA(buf), nil return ToECDSA(buf), nil
} }
// SaveECDSA saves a secp256k1 private key to the given file with restrictive
// permissions
func SaveECDSA(file string, key *ecdsa.PrivateKey) error {
return ioutil.WriteFile(file, FromECDSA(key), 0600)
}
func GenerateKey() (*ecdsa.PrivateKey, error) { func GenerateKey() (*ecdsa.PrivateKey, error) {
return ecdsa.GenerateKey(S256(), rand.Reader) return ecdsa.GenerateKey(S256(), rand.Reader)
} }
func SigToPub(hash, sig []byte) *ecdsa.PublicKey { func SigToPub(hash, sig []byte) *ecdsa.PublicKey {
s := Ecrecover(append(hash, sig...)) s := Ecrecover(hash, sig)
x, y := elliptic.Unmarshal(S256(), s) if s == nil || len(s) != 65 {
return nil
}
x, y := elliptic.Unmarshal(S256(), s)
return &ecdsa.PublicKey{S256(), x, y} return &ecdsa.PublicKey{S256(), x, y}
} }

View file

@ -85,6 +85,16 @@ func (k *Key) UnmarshalJSON(j []byte) (err error) {
return err return err
} }
func NewKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
id := uuid.NewRandom()
key := &Key{
Id: id,
Address: PubkeyToAddress(privateKeyECDSA.PublicKey),
PrivateKey: privateKeyECDSA,
}
return key
}
func NewKey(rand io.Reader) *Key { func NewKey(rand io.Reader) *Key {
randBytes := make([]byte, 64) randBytes := make([]byte, 64)
_, err := rand.Read(randBytes) _, err := rand.Read(randBytes)
@ -97,11 +107,5 @@ func NewKey(rand io.Reader) *Key {
panic("key generation: ecdsa.GenerateKey failed: " + err.Error()) panic("key generation: ecdsa.GenerateKey failed: " + err.Error())
} }
id := uuid.NewRandom() return NewKeyFromECDSA(privateKeyECDSA)
key := &Key{
Id: id,
Address: PubkeyToAddress(privateKeyECDSA.PublicKey),
PrivateKey: privateKeyECDSA,
}
return key
} }

View file

@ -31,9 +31,9 @@ var (
defaultBootNodes = []*discover.Node{ defaultBootNodes = []*discover.Node{
// ETH/DEV cmd/bootnode // ETH/DEV cmd/bootnode
discover.MustParseNode("enode://6cdd090303f394a1cac34ecc9f7cda18127eafa2a3a06de39f6d920b0e583e062a7362097c7c65ee490a758b442acd5c80c6fce4b148c6a391e946b45131365b@54.169.166.226:30303"), discover.MustParseNode("enode://09fbeec0d047e9a37e63f60f8618aa9df0e49271f3fadb2c070dc09e2099b95827b63a8b837c6fd01d0802d457dd83e3bd48bd3e6509f8209ed90dabbc30e3d3@52.16.188.185:30303"),
// ETH/DEV cpp-ethereum (poc-8.ethdev.com) // ETH/DEV cpp-ethereum (poc-9.ethdev.com)
discover.MustParseNode("enode://4a44599974518ea5b0f14c31c4463692ac0329cb84851f3435e6d1b18ee4eae4aa495f846a0fa1219bd58035671881d44423876e57db2abd57254d0197da0ebe@5.1.83.226:30303"), discover.MustParseNode("enode://487611428e6c99a11a9795a6abe7b529e81315ca6aad66e2a2fc76e3adf263faba0d35466c2f8f68d561dbefa8878d4df5f1f2ddb1fbeab7f42ffb8cd328bd4a@5.1.83.226:30303"),
} }
) )
@ -63,6 +63,7 @@ type Config struct {
Shh bool Shh bool
Dial bool Dial bool
Etherbase string
MinerThreads int MinerThreads int
AccountManager *accounts.Manager AccountManager *accounts.Manager
@ -140,6 +141,7 @@ type Ethereum struct {
Mining bool Mining bool
DataDir string DataDir string
etherbase common.Address
clientVersion string clientVersion string
ethVersionId int ethVersionId int
netVersionId int netVersionId int
@ -185,6 +187,7 @@ func New(config *Config) (*Ethereum, error) {
eventMux: &event.TypeMux{}, eventMux: &event.TypeMux{},
accountManager: config.AccountManager, accountManager: config.AccountManager,
DataDir: config.DataDir, DataDir: config.DataDir,
etherbase: common.HexToAddress(config.Etherbase),
clientVersion: config.Name, // TODO should separate from Name clientVersion: config.Name, // TODO should separate from Name
ethVersionId: config.ProtocolVersion, ethVersionId: config.ProtocolVersion,
netVersionId: config.NetworkId, netVersionId: config.NetworkId,
@ -297,15 +300,31 @@ func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) {
} }
func (s *Ethereum) StartMining() error { func (s *Ethereum) StartMining() error {
cb, err := s.accountManager.Coinbase() eb, err := s.Etherbase()
if err != nil { if err != nil {
servlogger.Errorf("Cannot start mining without coinbase: %v\n", err) err = fmt.Errorf("Cannot start mining without etherbase address: %v", err)
return fmt.Errorf("no coinbase: %v", err) servlogger.Errorln(err)
return err
} }
s.miner.Start(common.BytesToAddress(cb))
s.miner.Start(eb)
return nil return nil
} }
func (s *Ethereum) Etherbase() (eb common.Address, err error) {
eb = s.etherbase
if (eb == common.Address{}) {
var ebbytes []byte
ebbytes, err = s.accountManager.Primary()
eb = common.BytesToAddress(ebbytes)
if (eb == common.Address{}) {
err = fmt.Errorf("no accounts found")
}
}
return
}
func (s *Ethereum) StopMining() { s.miner.Stop() } func (s *Ethereum) StopMining() { s.miner.Stop() }
func (s *Ethereum) IsMining() bool { return s.miner.Mining() } func (s *Ethereum) IsMining() bool { return s.miner.Mining() }
func (s *Ethereum) Miner() *miner.Miner { return s.miner } func (s *Ethereum) Miner() *miner.Miner { return s.miner }

View file

@ -13,7 +13,7 @@ import (
) )
const ( const (
ProtocolVersion = 59 ProtocolVersion = 60
NetworkId = 0 NetworkId = 0
ProtocolLength = uint64(8) ProtocolLength = uint64(8)
ProtocolMaxMsgSize = 10 * 1024 * 1024 ProtocolMaxMsgSize = 10 * 1024 * 1024
@ -185,7 +185,10 @@ func (self *ethProtocol) handle() error {
if err := msg.Decode(&txs); err != nil { if err := msg.Decode(&txs); err != nil {
return self.protoError(ErrDecode, "msg %v: %v", msg, err) return self.protoError(ErrDecode, "msg %v: %v", msg, err)
} }
for _, tx := range txs { for i, tx := range txs {
if tx == nil {
return self.protoError(ErrDecode, "transaction %d is nil", i)
}
jsonlogger.LogJson(&logger.EthTxReceived{ jsonlogger.LogJson(&logger.EthTxReceived{
TxHash: tx.Hash().Hex(), TxHash: tx.Hash().Hex(),
RemoteId: self.peer.ID().String(), RemoteId: self.peer.ID().String(),
@ -268,6 +271,9 @@ func (self *ethProtocol) handle() error {
return self.protoError(ErrDecode, "msg %v: %v", msg, err) return self.protoError(ErrDecode, "msg %v: %v", msg, err)
} }
} }
if err := block.ValidateFields(); err != nil {
return self.protoError(ErrDecode, "block validation %v: %v", msg, err)
}
self.blockPool.AddBlock(&block, self.id) self.blockPool.AddBlock(&block, self.id)
} }
@ -276,6 +282,9 @@ func (self *ethProtocol) handle() error {
if err := msg.Decode(&request); err != nil { if err := msg.Decode(&request); err != nil {
return self.protoError(ErrDecode, "%v: %v", msg, err) return self.protoError(ErrDecode, "%v: %v", msg, err)
} }
if err := request.Block.ValidateFields(); err != nil {
return self.protoError(ErrDecode, "block validation %v: %v", msg, err)
}
hash := request.Block.Hash() hash := request.Block.Hash()
_, chainHead, _ := self.chainManager.Status() _, chainHead, _ := self.chainManager.Status()

View file

@ -63,6 +63,10 @@ func (self *testChainManager) GetBlockHashesFromHash(hash common.Hash, amount ui
func (self *testChainManager) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) { func (self *testChainManager) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) {
if self.status != nil { if self.status != nil {
td, currentBlock, genesisBlock = self.status() td, currentBlock, genesisBlock = self.status()
} else {
td = common.Big1
currentBlock = common.Hash{1}
genesisBlock = common.Hash{2}
} }
return return
} }
@ -163,14 +167,29 @@ func (self *ethProtocolTester) run() {
self.quit <- err self.quit <- err
} }
func (self *ethProtocolTester) handshake(t *testing.T, mock bool) {
td, currentBlock, genesis := self.chainManager.Status()
// first outgoing msg should be StatusMsg.
err := p2p.ExpectMsg(self, StatusMsg, &statusMsgData{
ProtocolVersion: ProtocolVersion,
NetworkId: NetworkId,
TD: td,
CurrentBlock: currentBlock,
GenesisBlock: genesis,
})
if err != nil {
t.Fatalf("incorrect outgoing status: %v", err)
}
if mock {
go p2p.Send(self, StatusMsg, &statusMsgData{ProtocolVersion, NetworkId, td, currentBlock, genesis})
}
}
func TestStatusMsgErrors(t *testing.T) { func TestStatusMsgErrors(t *testing.T) {
logInit() logInit()
eth := newEth(t) eth := newEth(t)
td := common.Big1
currentBlock := common.Hash{1}
genesis := common.Hash{2}
eth.chainManager.status = func() (*big.Int, common.Hash, common.Hash) { return td, currentBlock, genesis }
go eth.run() go eth.run()
td, currentBlock, genesis := eth.chainManager.Status()
tests := []struct { tests := []struct {
code uint64 code uint64
@ -195,18 +214,7 @@ func TestStatusMsgErrors(t *testing.T) {
}, },
} }
for _, test := range tests { for _, test := range tests {
// first outgoing msg should be StatusMsg. eth.handshake(t, false)
err := p2p.ExpectMsg(eth, StatusMsg, &statusMsgData{
ProtocolVersion: ProtocolVersion,
NetworkId: NetworkId,
TD: td,
CurrentBlock: currentBlock,
GenesisBlock: genesis,
})
if err != nil {
t.Fatalf("incorrect outgoing status: %v", err)
}
// the send call might hang until reset because // the send call might hang until reset because
// the protocol might not read the payload. // the protocol might not read the payload.
go p2p.Send(eth, test.code, test.data) go p2p.Send(eth, test.code, test.data)
@ -216,3 +224,177 @@ func TestStatusMsgErrors(t *testing.T) {
go eth.run() go eth.run()
} }
} }
func TestNewBlockMsg(t *testing.T) {
// logInit()
eth := newEth(t)
var disconnected bool
eth.blockPool.removePeer = func(peerId string) {
disconnected = true
}
go eth.run()
eth.handshake(t, true)
err := p2p.ExpectMsg(eth, TxMsg, []interface{}{})
if err != nil {
t.Errorf("transactions expected, got %v", err)
}
var tds = make(chan *big.Int)
eth.blockPool.addPeer = func(td *big.Int, currentBlock common.Hash, peerId string, requestHashes func(common.Hash) error, requestBlocks func([]common.Hash) error, peerError func(*errs.Error)) (best bool, suspended bool) {
tds <- td
return
}
var delay = 1 * time.Second
// eth.reset()
block := types.NewBlock(common.Hash{1}, common.Address{1}, common.Hash{1}, common.Big1, 1, "extra")
go p2p.Send(eth, NewBlockMsg, &newBlockMsgData{Block: block})
timer := time.After(delay)
select {
case td := <-tds:
if td.Cmp(common.Big0) != 0 {
t.Errorf("incorrect td %v, expected %v", td, common.Big0)
}
case <-timer:
t.Errorf("no td recorded after %v", delay)
return
case err := <-eth.quit:
t.Errorf("no error expected, got %v", err)
return
}
go p2p.Send(eth, NewBlockMsg, &newBlockMsgData{block, common.Big2})
timer = time.After(delay)
select {
case td := <-tds:
if td.Cmp(common.Big2) != 0 {
t.Errorf("incorrect td %v, expected %v", td, common.Big2)
}
case <-timer:
t.Errorf("no td recorded after %v", delay)
return
case err := <-eth.quit:
t.Errorf("no error expected, got %v", err)
return
}
go p2p.Send(eth, NewBlockMsg, []interface{}{})
// Block.DecodeRLP: validation failed: header is nil
eth.checkError(ErrDecode, delay)
}
func TestBlockMsg(t *testing.T) {
// logInit()
eth := newEth(t)
blocks := make(chan *types.Block)
eth.blockPool.addBlock = func(block *types.Block, peerId string) (err error) {
blocks <- block
return
}
var disconnected bool
eth.blockPool.removePeer = func(peerId string) {
disconnected = true
}
go eth.run()
eth.handshake(t, true)
err := p2p.ExpectMsg(eth, TxMsg, []interface{}{})
if err != nil {
t.Errorf("transactions expected, got %v", err)
}
var delay = 3 * time.Second
// eth.reset()
newblock := func(i int64) *types.Block {
return types.NewBlock(common.Hash{byte(i)}, common.Address{byte(i)}, common.Hash{byte(i)}, big.NewInt(i), uint64(i), string(i))
}
b := newblock(0)
b.Header().Difficulty = nil // check if nil as *big.Int decodes as 0
go p2p.Send(eth, BlocksMsg, types.Blocks{b, newblock(1), newblock(2)})
timer := time.After(delay)
for i := int64(0); i < 3; i++ {
select {
case block := <-blocks:
if (block.ParentHash() != common.Hash{byte(i)}) {
t.Errorf("incorrect block %v, expected %v", block.ParentHash(), common.Hash{byte(i)})
}
if block.Difficulty().Cmp(big.NewInt(i)) != 0 {
t.Errorf("incorrect block %v, expected %v", block.Difficulty(), big.NewInt(i))
}
case <-timer:
t.Errorf("no td recorded after %v", delay)
return
case err := <-eth.quit:
t.Errorf("no error expected, got %v", err)
return
}
}
go p2p.Send(eth, BlocksMsg, []interface{}{[]interface{}{}})
eth.checkError(ErrDecode, delay)
if !disconnected {
t.Errorf("peer not disconnected after error")
}
// test empty transaction
eth.reset()
go eth.run()
eth.handshake(t, true)
err = p2p.ExpectMsg(eth, TxMsg, []interface{}{})
if err != nil {
t.Errorf("transactions expected, got %v", err)
}
b = newblock(0)
b.AddTransaction(nil)
go p2p.Send(eth, BlocksMsg, types.Blocks{b})
eth.checkError(ErrDecode, delay)
}
func TestTransactionsMsg(t *testing.T) {
logInit()
eth := newEth(t)
txs := make(chan *types.Transaction)
eth.txPool.addTransactions = func(t []*types.Transaction) {
for _, tx := range t {
txs <- tx
}
}
go eth.run()
eth.handshake(t, true)
err := p2p.ExpectMsg(eth, TxMsg, []interface{}{})
if err != nil {
t.Errorf("transactions expected, got %v", err)
}
var delay = 3 * time.Second
tx := &types.Transaction{}
go p2p.Send(eth, TxMsg, []interface{}{tx, tx})
timer := time.After(delay)
for i := int64(0); i < 2; i++ {
select {
case <-txs:
case <-timer:
return
case err := <-eth.quit:
t.Errorf("no error expected, got %v", err)
return
}
}
go p2p.Send(eth, TxMsg, []interface{}{[]interface{}{}})
eth.checkError(ErrDecode, delay)
}

65
generators/defaults.go Normal file
View file

@ -0,0 +1,65 @@
//go:generate go run defaults.go default.json defs.go
package main //build !none
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
"strings"
)
func fatal(str string, v ...interface{}) {
fmt.Fprintf(os.Stderr, str, v...)
os.Exit(1)
}
type setting struct {
Value int64 `json:"v"`
Comment string `json:"d"`
}
func main() {
if len(os.Args) < 3 {
fatal("usage %s <input> <output>\n", os.Args[0])
}
content, err := ioutil.ReadFile(os.Args[1])
if err != nil {
fatal("error reading file %v\n", err)
}
m := make(map[string]setting)
json.Unmarshal(content, &m)
filepath := path.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "params", os.Args[2])
output, err := os.OpenFile(filepath, os.O_RDWR|os.O_CREATE, os.ModePerm /*0777*/)
if err != nil {
fatal("error opening file for writing %v\n", err)
}
output.WriteString(`// DO NOT EDIT!!!
// AUTOGENERATED FROM generators/defaults.go
package params
import "math/big"
var (
`)
for name, setting := range m {
output.WriteString(fmt.Sprintf("%s=big.NewInt(%d) // %s\n", strings.Title(name), setting.Value, setting.Comment))
}
output.WriteString(")\n")
output.Close()
cmd := exec.Command("gofmt", "-w", filepath)
if err := cmd.Run(); err != nil {
fatal("gofmt failed: %v\n", err)
}
}

File diff suppressed because one or more lines are too long

View file

@ -2,9 +2,9 @@ package jsre
import ( import (
"github.com/robertkrimen/otto" "github.com/robertkrimen/otto"
"io/ioutil"
"os"
"testing" "testing"
"github.com/ethereum/go-ethereum/common"
) )
type testNativeObjectBinding struct { type testNativeObjectBinding struct {
@ -26,7 +26,7 @@ func (no *testNativeObjectBinding) TestMethod(call otto.FunctionCall) otto.Value
func TestExec(t *testing.T) { func TestExec(t *testing.T) {
jsre := New("/tmp") jsre := New("/tmp")
common.WriteFile("/tmp/test.js", []byte(`msg = "testMsg"`)) ioutil.WriteFile("/tmp/test.js", []byte(`msg = "testMsg"`), os.ModePerm)
err := jsre.Exec("test.js") err := jsre.Exec("test.js")
if err != nil { if err != nil {
t.Errorf("expected no error, got %v", err) t.Errorf("expected no error, got %v", err)
@ -64,7 +64,7 @@ func TestBind(t *testing.T) {
func TestLoadScript(t *testing.T) { func TestLoadScript(t *testing.T) {
jsre := New("/tmp") jsre := New("/tmp")
common.WriteFile("/tmp/test.js", []byte(`msg = "testMsg"`)) ioutil.WriteFile("/tmp/test.js", []byte(`msg = "testMsg"`), os.ModePerm)
_, err := jsre.Run(`loadScript("test.js")`) _, err := jsre.Run(`loadScript("test.js")`)
if err != nil { if err != nil {
t.Errorf("expected no error, got %v", err) t.Errorf("expected no error, got %v", err)

View file

@ -1,12 +1,15 @@
package miner package miner
import ( import (
"sync"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/pow"
) )
type CpuMiner struct { type CpuMiner struct {
chMu sync.Mutex
c chan *types.Block c chan *types.Block
quit chan struct{} quit chan struct{}
quitCurrentOp chan struct{} quitCurrentOp chan struct{}
@ -43,16 +46,13 @@ func (self *CpuMiner) Start() {
} }
func (self *CpuMiner) update() { func (self *CpuMiner) update() {
justStarted := true
out: out:
for { for {
select { select {
case block := <-self.c: case block := <-self.c:
if justStarted { self.chMu.Lock()
justStarted = true
} else {
self.quitCurrentOp <- struct{}{} self.quitCurrentOp <- struct{}{}
} self.chMu.Unlock()
go self.mine(block) go self.mine(block)
case <-self.quit: case <-self.quit:
@ -60,6 +60,7 @@ out:
} }
} }
//close(self.quitCurrentOp)
done: done:
// Empty channel // Empty channel
for { for {
@ -74,13 +75,21 @@ done:
} }
func (self *CpuMiner) mine(block *types.Block) { func (self *CpuMiner) mine(block *types.Block) {
minerlogger.Infof("(re)started agent[%d]. mining...\n", self.index) minerlogger.Debugf("(re)started agent[%d]. mining...\n", self.index)
// Reset the channel
self.chMu.Lock()
self.quitCurrentOp = make(chan struct{}, 1)
self.chMu.Unlock()
// Mine
nonce, mixDigest, _ := self.pow.Search(block, self.quitCurrentOp) nonce, mixDigest, _ := self.pow.Search(block, self.quitCurrentOp)
if nonce != 0 { if nonce != 0 {
block.SetNonce(nonce) block.SetNonce(nonce)
block.Header().MixDigest = common.BytesToHash(mixDigest) block.Header().MixDigest = common.BytesToHash(mixDigest)
self.returnCh <- block self.returnCh <- block
//self.returnCh <- Work{block.Number().Uint64(), nonce, mixDigest, seedHash} } else {
self.returnCh <- nil
} }
} }

View file

@ -50,6 +50,7 @@ out:
break out break out
case work := <-a.workCh: case work := <-a.workCh:
a.work = work a.work = work
a.returnCh <- nil
} }
} }
} }

View file

@ -5,6 +5,7 @@ import (
"math/big" "math/big"
"sort" "sort"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -59,12 +60,13 @@ type Agent interface {
type worker struct { type worker struct {
mu sync.Mutex mu sync.Mutex
agents []Agent agents []Agent
recv chan *types.Block recv chan *types.Block
mux *event.TypeMux mux *event.TypeMux
quit chan struct{} quit chan struct{}
pow pow.PoW pow pow.PoW
atWork int atWork int64
eth core.Backend eth core.Backend
chain *core.ChainManager chain *core.ChainManager
@ -107,7 +109,7 @@ func (self *worker) start() {
func (self *worker) stop() { func (self *worker) stop() {
self.mining = false self.mining = false
self.atWork = 0 atomic.StoreInt64(&self.atWork, 0)
close(self.quit) close(self.quit)
} }
@ -135,9 +137,6 @@ out:
self.uncleMu.Unlock() self.uncleMu.Unlock()
} }
if self.atWork == 0 {
self.commitNewWork()
}
case <-self.quit: case <-self.quit:
// stop all agents // stop all agents
for _, agent := range self.agents { for _, agent := range self.agents {
@ -146,6 +145,11 @@ out:
break out break out
case <-timer.C: case <-timer.C:
minerlogger.Infoln("Hash rate:", self.HashRate(), "Khash") minerlogger.Infoln("Hash rate:", self.HashRate(), "Khash")
// XXX In case all mined a possible uncle
if atomic.LoadInt64(&self.atWork) == 0 {
self.commitNewWork()
}
} }
} }
@ -155,6 +159,12 @@ out:
func (self *worker) wait() { func (self *worker) wait() {
for { for {
for block := range self.recv { for block := range self.recv {
atomic.AddInt64(&self.atWork, -1)
if block == nil {
continue
}
if err := self.chain.InsertChain(types.Blocks{block}); err == nil { if err := self.chain.InsertChain(types.Blocks{block}); err == nil {
for _, uncle := range block.Uncles() { for _, uncle := range block.Uncles() {
delete(self.possibleUncles, uncle.Hash()) delete(self.possibleUncles, uncle.Hash())
@ -170,7 +180,6 @@ func (self *worker) wait() {
} else { } else {
self.commitNewWork() self.commitNewWork()
} }
self.atWork--
} }
} }
} }
@ -182,8 +191,9 @@ func (self *worker) push() {
// push new work to agents // push new work to agents
for _, agent := range self.agents { for _, agent := range self.agents {
atomic.AddInt64(&self.atWork, 1)
agent.Work() <- self.current.block.Copy() agent.Work() <- self.current.block.Copy()
self.atWork++
} }
} }
} }
@ -260,9 +270,9 @@ gasLimit:
self.current.block.SetUncles(uncles) self.current.block.SetUncles(uncles)
self.current.state.AddBalance(self.coinbase, core.BlockReward) core.AccumulateRewards(self.current.state, self.current.block)
self.current.state.Update(common.Big0) self.current.state.Update()
self.push() self.push()
} }
@ -287,9 +297,6 @@ func (self *worker) commitUncle(uncle *types.Header) error {
return core.UncleError(fmt.Sprintf("Uncle already in family (%x)", uncle.Hash())) return core.UncleError(fmt.Sprintf("Uncle already in family (%x)", uncle.Hash()))
} }
self.current.state.AddBalance(uncle.Coinbase, uncleReward)
self.current.state.AddBalance(self.coinbase, inclusionReward)
return nil return nil
} }

View file

@ -13,6 +13,8 @@ import (
"net/url" "net/url"
"strconv" "strconv"
"strings" "strings"
"sync"
"sync/atomic"
"time" "time"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -30,7 +32,8 @@ type Node struct {
DiscPort int // UDP listening port for discovery protocol DiscPort int // UDP listening port for discovery protocol
TCPPort int // TCP listening port for RLPx TCPPort int // TCP listening port for RLPx
active time.Time // this must be set/read using atomic load and store.
activeStamp int64
} }
func newNode(id NodeID, addr *net.UDPAddr) *Node { func newNode(id NodeID, addr *net.UDPAddr) *Node {
@ -39,7 +42,6 @@ func newNode(id NodeID, addr *net.UDPAddr) *Node {
IP: addr.IP, IP: addr.IP,
DiscPort: addr.Port, DiscPort: addr.Port,
TCPPort: addr.Port, TCPPort: addr.Port,
active: time.Now(),
} }
} }
@ -48,6 +50,20 @@ func (n *Node) isValid() bool {
return !n.IP.IsMulticast() && !n.IP.IsUnspecified() && n.TCPPort != 0 && n.DiscPort != 0 return !n.IP.IsMulticast() && !n.IP.IsUnspecified() && n.TCPPort != 0 && n.DiscPort != 0
} }
func (n *Node) bumpActive() {
stamp := time.Now().Unix()
atomic.StoreInt64(&n.activeStamp, stamp)
}
func (n *Node) active() time.Time {
stamp := atomic.LoadInt64(&n.activeStamp)
return time.Unix(stamp, 0)
}
func (n *Node) addr() *net.UDPAddr {
return &net.UDPAddr{IP: n.IP, Port: n.DiscPort}
}
// The string representation of a Node is a URL. // The string representation of a Node is a URL.
// Please see ParseNode for a description of the format. // Please see ParseNode for a description of the format.
func (n *Node) String() string { func (n *Node) String() string {
@ -304,3 +320,26 @@ func randomID(a NodeID, n int) (b NodeID) {
} }
return b return b
} }
// nodeDB stores all nodes we know about.
type nodeDB struct {
mu sync.RWMutex
byID map[NodeID]*Node
}
func (db *nodeDB) get(id NodeID) *Node {
db.mu.RLock()
defer db.mu.RUnlock()
return db.byID[id]
}
func (db *nodeDB) add(id NodeID, addr *net.UDPAddr, tcpPort uint16) *Node {
db.mu.Lock()
defer db.mu.Unlock()
if db.byID == nil {
db.byID = make(map[NodeID]*Node)
}
n := &Node{ID: id, IP: addr.IP, DiscPort: addr.Port, TCPPort: int(tcpPort)}
db.byID[n.ID] = n
return n
}

View file

@ -17,6 +17,7 @@ const (
alpha = 3 // Kademlia concurrency factor alpha = 3 // Kademlia concurrency factor
bucketSize = 16 // Kademlia bucket size bucketSize = 16 // Kademlia bucket size
nBuckets = nodeIDBits + 1 // Number of buckets nBuckets = nodeIDBits + 1 // Number of buckets
maxBondingPingPongs = 10
) )
type Table struct { type Table struct {
@ -24,27 +25,50 @@ type Table struct {
buckets [nBuckets]*bucket // index of known nodes by distance buckets [nBuckets]*bucket // index of known nodes by distance
nursery []*Node // bootstrap nodes nursery []*Node // bootstrap nodes
bondmu sync.Mutex
bonding map[NodeID]*bondproc
bondslots chan struct{} // limits total number of active bonding processes
net transport net transport
self *Node // metadata of the local node self *Node // metadata of the local node
db *nodeDB
}
type bondproc struct {
err error
n *Node
done chan struct{}
} }
// transport is implemented by the UDP transport. // transport is implemented by the UDP transport.
// it is an interface so we can test without opening lots of UDP // it is an interface so we can test without opening lots of UDP
// sockets and without generating a private key. // sockets and without generating a private key.
type transport interface { type transport interface {
ping(*Node) error ping(NodeID, *net.UDPAddr) error
findnode(e *Node, target NodeID) ([]*Node, error) waitping(NodeID) error
findnode(toid NodeID, addr *net.UDPAddr, target NodeID) ([]*Node, error)
close() close()
} }
// bucket contains nodes, ordered by their last activity. // bucket contains nodes, ordered by their last activity.
// the entry that was most recently active is the last element
// in entries.
type bucket struct { type bucket struct {
lastLookup time.Time lastLookup time.Time
entries []*Node entries []*Node
} }
func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr) *Table { func newTable(t transport, ourID NodeID, ourAddr *net.UDPAddr) *Table {
tab := &Table{net: t, self: newNode(ourID, ourAddr)} tab := &Table{
net: t,
db: new(nodeDB),
self: newNode(ourID, ourAddr),
bonding: make(map[NodeID]*bondproc),
bondslots: make(chan struct{}, maxBondingPingPongs),
}
for i := 0; i < cap(tab.bondslots); i++ {
tab.bondslots <- struct{}{}
}
for i := range tab.buckets { for i := range tab.buckets {
tab.buckets[i] = new(bucket) tab.buckets[i] = new(bucket)
} }
@ -107,8 +131,8 @@ func (tab *Table) Lookup(target NodeID) []*Node {
asked[n.ID] = true asked[n.ID] = true
pendingQueries++ pendingQueries++
go func() { go func() {
result, _ := tab.net.findnode(n, target) r, _ := tab.net.findnode(n.ID, n.addr(), target)
reply <- result reply <- tab.bondall(r)
}() }()
} }
} }
@ -116,13 +140,11 @@ func (tab *Table) Lookup(target NodeID) []*Node {
// we have asked all closest nodes, stop the search // we have asked all closest nodes, stop the search
break break
} }
// wait for the next reply // wait for the next reply
for _, n := range <-reply { for _, n := range <-reply {
cn := n if n != nil && !seen[n.ID] {
if !seen[n.ID] {
seen[n.ID] = true seen[n.ID] = true
result.push(cn, bucketSize) result.push(n, bucketSize)
} }
} }
pendingQueries-- pendingQueries--
@ -145,8 +167,9 @@ func (tab *Table) refresh() {
result := tab.Lookup(randomID(tab.self.ID, ld)) result := tab.Lookup(randomID(tab.self.ID, ld))
if len(result) == 0 { if len(result) == 0 {
// bootstrap the table with a self lookup // bootstrap the table with a self lookup
all := tab.bondall(tab.nursery)
tab.mutex.Lock() tab.mutex.Lock()
tab.add(tab.nursery) tab.add(all)
tab.mutex.Unlock() tab.mutex.Unlock()
tab.Lookup(tab.self.ID) tab.Lookup(tab.self.ID)
// TODO: the Kademlia paper says that we're supposed to perform // TODO: the Kademlia paper says that we're supposed to perform
@ -176,45 +199,105 @@ func (tab *Table) len() (n int) {
return n return n
} }
// bumpOrAdd updates the activity timestamp for the given node and // bondall bonds with all given nodes concurrently and returns
// attempts to insert the node into a bucket. The returned Node might // those nodes for which bonding has probably succeeded.
// not be part of the table. The caller must hold tab.mutex. func (tab *Table) bondall(nodes []*Node) (result []*Node) {
func (tab *Table) bumpOrAdd(node NodeID, from *net.UDPAddr) (n *Node) { rc := make(chan *Node, len(nodes))
b := tab.buckets[logdist(tab.self.ID, node)] for i := range nodes {
if n = b.bump(node); n == nil { go func(n *Node) {
n = newNode(node, from) nn, _ := tab.bond(false, n.ID, n.addr(), uint16(n.TCPPort))
if len(b.entries) == bucketSize { rc <- nn
tab.pingReplace(n, b) }(nodes[i])
} else { }
b.entries = append(b.entries, n) for _ = range nodes {
if n := <-rc; n != nil {
result = append(result, n)
} }
} }
return n return result
} }
func (tab *Table) pingReplace(n *Node, b *bucket) { // bond ensures the local node has a bond with the given remote node.
old := b.entries[bucketSize-1] // It also attempts to insert the node into the table if bonding succeeds.
go func() { // The caller must not hold tab.mutex.
if err := tab.net.ping(old); err == nil { //
// it responded, we don't need to replace it. // A bond is must be established before sending findnode requests.
// Both sides must have completed a ping/pong exchange for a bond to
// exist. The total number of active bonding processes is limited in
// order to restrain network use.
//
// bond is meant to operate idempotently in that bonding with a remote
// node which still remembers a previously established bond will work.
// The remote node will simply not send a ping back, causing waitping
// to time out.
//
// If pinged is true, the remote node has just pinged us and one half
// of the process can be skipped.
func (tab *Table) bond(pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16) (*Node, error) {
var n *Node
if n = tab.db.get(id); n == nil {
tab.bondmu.Lock()
w := tab.bonding[id]
if w != nil {
// Wait for an existing bonding process to complete.
tab.bondmu.Unlock()
<-w.done
} else {
// Register a new bonding process.
w = &bondproc{done: make(chan struct{})}
tab.bonding[id] = w
tab.bondmu.Unlock()
// Do the ping/pong. The result goes into w.
tab.pingpong(w, pinged, id, addr, tcpPort)
// Unregister the process after it's done.
tab.bondmu.Lock()
delete(tab.bonding, id)
tab.bondmu.Unlock()
}
n = w.n
if w.err != nil {
return nil, w.err
}
}
tab.mutex.Lock()
defer tab.mutex.Unlock()
if b := tab.buckets[logdist(tab.self.ID, n.ID)]; !b.bump(n) {
tab.pingreplace(n, b)
}
return n, nil
}
func (tab *Table) pingpong(w *bondproc, pinged bool, id NodeID, addr *net.UDPAddr, tcpPort uint16) {
<-tab.bondslots
defer func() { tab.bondslots <- struct{}{} }()
if w.err = tab.net.ping(id, addr); w.err != nil {
close(w.done)
return return
} }
// it didn't respond, replace the node if it is still the oldest node. if !pinged {
tab.mutex.Lock() // Give the remote node a chance to ping us before we start
if len(b.entries) > 0 && b.entries[len(b.entries)-1] == old { // sending findnode requests. If they still remember us,
// slide down other entries and put the new one in front. // waitping will simply time out.
// TODO: insert in correct position to keep the order tab.net.waitping(id)
copy(b.entries[1:], b.entries)
b.entries[0] = n
} }
tab.mutex.Unlock() w.n = tab.db.add(id, addr, tcpPort)
}() close(w.done)
} }
// bump updates the activity timestamp for the given node. func (tab *Table) pingreplace(new *Node, b *bucket) {
// The caller must hold tab.mutex. if len(b.entries) == bucketSize {
func (tab *Table) bump(node NodeID) { oldest := b.entries[bucketSize-1]
tab.buckets[logdist(tab.self.ID, node)].bump(node) if err := tab.net.ping(oldest.ID, oldest.addr()); err == nil {
// The node responded, we don't need to replace it.
return
}
} else {
// Add a slot at the end so the last entry doesn't
// fall off when adding the new node.
b.entries = append(b.entries, nil)
}
copy(b.entries[1:], b.entries)
b.entries[0] = new
} }
// add puts the entries into the table if their corresponding // add puts the entries into the table if their corresponding
@ -240,17 +323,17 @@ outer:
} }
} }
func (b *bucket) bump(id NodeID) *Node { func (b *bucket) bump(n *Node) bool {
for i, n := range b.entries { for i := range b.entries {
if n.ID == id { if b.entries[i].ID == n.ID {
n.active = time.Now() n.bumpActive()
// move it to the front // move it to the front
copy(b.entries[1:], b.entries[:i+1]) copy(b.entries[1:], b.entries[:i])
b.entries[0] = n b.entries[0] = n
return n return true
} }
} }
return nil return false
} }
// nodesByDistance is a list of nodes, ordered by // nodesByDistance is a list of nodes, ordered by

View file

@ -2,79 +2,110 @@ package discover
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"errors"
"fmt" "fmt"
"math/rand" "math/rand"
"net" "net"
"reflect" "reflect"
"testing" "testing"
"testing/quick" "testing/quick"
"time"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
) )
func TestTable_bumpOrAddBucketAssign(t *testing.T) { func TestTable_pingReplace(t *testing.T) {
tab := newTable(nil, NodeID{}, &net.UDPAddr{}) doit := func(newNodeIsResponding, lastInBucketIsResponding bool) {
for i := 1; i < len(tab.buckets); i++ { transport := newPingRecorder()
tab.bumpOrAdd(randomID(tab.self.ID, i), &net.UDPAddr{}) tab := newTable(transport, NodeID{}, &net.UDPAddr{})
}
for i, b := range tab.buckets {
if i > 0 && len(b.entries) != 1 {
t.Errorf("bucket %d has %d entries, want 1", i, len(b.entries))
}
}
}
func TestTable_bumpOrAddPingReplace(t *testing.T) {
pingC := make(pingC)
tab := newTable(pingC, NodeID{}, &net.UDPAddr{})
last := fillBucket(tab, 200) last := fillBucket(tab, 200)
pingSender := randomID(tab.self.ID, 200)
// this bumpOrAdd should not replace the last node // this gotPing should replace the last node
// because the node replies to ping. // if the last node is not responding.
new := tab.bumpOrAdd(randomID(tab.self.ID, 200), &net.UDPAddr{}) transport.responding[last.ID] = lastInBucketIsResponding
transport.responding[pingSender] = newNodeIsResponding
tab.bond(true, pingSender, &net.UDPAddr{}, 0)
pinged := <-pingC // first ping goes to sender (bonding pingback)
if pinged != last.ID { if !transport.pinged[pingSender] {
t.Fatalf("pinged wrong node: %v\nwant %v", pinged, last.ID) t.Error("table did not ping back sender")
}
if newNodeIsResponding {
// second ping goes to oldest node in bucket
// to see whether it is still alive.
if !transport.pinged[last.ID] {
t.Error("table did not ping last node in bucket")
}
} }
tab.mutex.Lock() tab.mutex.Lock()
defer tab.mutex.Unlock() defer tab.mutex.Unlock()
if l := len(tab.buckets[200].entries); l != bucketSize { if l := len(tab.buckets[200].entries); l != bucketSize {
t.Errorf("wrong bucket size after bumpOrAdd: got %d, want %d", bucketSize, l) t.Errorf("wrong bucket size after gotPing: got %d, want %d", bucketSize, l)
} }
if lastInBucketIsResponding || !newNodeIsResponding {
if !contains(tab.buckets[200].entries, last.ID) { if !contains(tab.buckets[200].entries, last.ID) {
t.Error("last entry was removed") t.Error("last entry was removed")
} }
if contains(tab.buckets[200].entries, new.ID) { if contains(tab.buckets[200].entries, pingSender) {
t.Error("new entry was added") t.Error("new entry was added")
} }
} } else {
func TestTable_bumpOrAddPingTimeout(t *testing.T) {
tab := newTable(pingC(nil), NodeID{}, &net.UDPAddr{})
last := fillBucket(tab, 200)
// this bumpOrAdd should replace the last node
// because the node does not reply to ping.
new := tab.bumpOrAdd(randomID(tab.self.ID, 200), &net.UDPAddr{})
// wait for async bucket update. damn. this needs to go away.
time.Sleep(2 * time.Millisecond)
tab.mutex.Lock()
defer tab.mutex.Unlock()
if l := len(tab.buckets[200].entries); l != bucketSize {
t.Errorf("wrong bucket size after bumpOrAdd: got %d, want %d", bucketSize, l)
}
if contains(tab.buckets[200].entries, last.ID) { if contains(tab.buckets[200].entries, last.ID) {
t.Error("last entry was not removed") t.Error("last entry was not removed")
} }
if !contains(tab.buckets[200].entries, new.ID) { if !contains(tab.buckets[200].entries, pingSender) {
t.Error("new entry was not added") t.Error("new entry was not added")
} }
}
}
doit(true, true)
doit(false, true)
doit(false, true)
doit(false, false)
}
func TestBucket_bumpNoDuplicates(t *testing.T) {
t.Parallel()
cfg := &quick.Config{
MaxCount: 1000,
Rand: quickrand,
Values: func(args []reflect.Value, rand *rand.Rand) {
// generate a random list of nodes. this will be the content of the bucket.
n := rand.Intn(bucketSize-1) + 1
nodes := make([]*Node, n)
for i := range nodes {
nodes[i] = &Node{ID: randomID(NodeID{}, 200)}
}
args[0] = reflect.ValueOf(nodes)
// generate random bump positions.
bumps := make([]int, rand.Intn(100))
for i := range bumps {
bumps[i] = rand.Intn(len(nodes))
}
args[1] = reflect.ValueOf(bumps)
},
}
prop := func(nodes []*Node, bumps []int) (ok bool) {
b := &bucket{entries: make([]*Node, len(nodes))}
copy(b.entries, nodes)
for i, pos := range bumps {
b.bump(b.entries[pos])
if hasDuplicates(b.entries) {
t.Logf("bucket has duplicates after %d/%d bumps:", i+1, len(bumps))
for _, n := range b.entries {
t.Logf(" %p", n)
}
return false
}
}
return true
}
if err := quick.Check(prop, cfg); err != nil {
t.Error(err)
}
} }
func fillBucket(tab *Table, ld int) (last *Node) { func fillBucket(tab *Table, ld int) (last *Node) {
@ -85,44 +116,27 @@ func fillBucket(tab *Table, ld int) (last *Node) {
return b.entries[bucketSize-1] return b.entries[bucketSize-1]
} }
type pingC chan NodeID type pingRecorder struct{ responding, pinged map[NodeID]bool }
func (t pingC) findnode(n *Node, target NodeID) ([]*Node, error) { func newPingRecorder() *pingRecorder {
return &pingRecorder{make(map[NodeID]bool), make(map[NodeID]bool)}
}
func (t *pingRecorder) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node, error) {
panic("findnode called on pingRecorder") panic("findnode called on pingRecorder")
} }
func (t pingC) close() { func (t *pingRecorder) close() {
panic("close called on pingRecorder") panic("close called on pingRecorder")
} }
func (t pingC) ping(n *Node) error { func (t *pingRecorder) waitping(from NodeID) error {
if t == nil { return nil // remote always pings
return errTimeout
}
t <- n.ID
return nil
} }
func (t *pingRecorder) ping(toid NodeID, toaddr *net.UDPAddr) error {
func TestTable_bump(t *testing.T) { t.pinged[toid] = true
tab := newTable(nil, NodeID{}, &net.UDPAddr{}) if t.responding[toid] {
return nil
// add an old entry and two recent ones } else {
oldactive := time.Now().Add(-2 * time.Minute) return errTimeout
old := &Node{ID: randomID(tab.self.ID, 200), active: oldactive}
others := []*Node{
&Node{ID: randomID(tab.self.ID, 200), active: time.Now()},
&Node{ID: randomID(tab.self.ID, 200), active: time.Now()},
}
tab.add(append(others, old))
if tab.buckets[200].entries[0] == old {
t.Fatal("old entry is at front of bucket")
}
// bumping the old entry should move it to the front
tab.bump(old.ID)
if old.active == oldactive {
t.Error("activity timestamp not updated")
}
if tab.buckets[200].entries[0] != old {
t.Errorf("bumped entry did not move to the front of bucket")
} }
} }
@ -210,7 +224,7 @@ func TestTable_Lookup(t *testing.T) {
t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results) t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results)
} }
// seed table with initial node (otherwise lookup will terminate immediately) // seed table with initial node (otherwise lookup will terminate immediately)
tab.bumpOrAdd(randomID(target, 200), &net.UDPAddr{Port: 200}) tab.add([]*Node{newNode(randomID(target, 200), &net.UDPAddr{Port: 200})})
results := tab.Lookup(target) results := tab.Lookup(target)
t.Logf("results:") t.Logf("results:")
@ -238,16 +252,16 @@ type findnodeOracle struct {
target NodeID target NodeID
} }
func (t findnodeOracle) findnode(n *Node, target NodeID) ([]*Node, error) { func (t findnodeOracle) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node, error) {
t.t.Logf("findnode query at dist %d", n.DiscPort) t.t.Logf("findnode query at dist %d", toaddr.Port)
// current log distance is encoded in port number // current log distance is encoded in port number
var result []*Node var result []*Node
switch n.DiscPort { switch toaddr.Port {
case 0: case 0:
panic("query to node at distance 0") panic("query to node at distance 0")
default: default:
// TODO: add more randomness to distances // TODO: add more randomness to distances
next := n.DiscPort - 1 next := toaddr.Port - 1
for i := 0; i < bucketSize; i++ { for i := 0; i < bucketSize; i++ {
result = append(result, &Node{ID: randomID(t.target, next), DiscPort: next}) result = append(result, &Node{ID: randomID(t.target, next), DiscPort: next})
} }
@ -256,10 +270,8 @@ func (t findnodeOracle) findnode(n *Node, target NodeID) ([]*Node, error) {
} }
func (t findnodeOracle) close() {} func (t findnodeOracle) close() {}
func (t findnodeOracle) waitping(from NodeID) error { return nil }
func (t findnodeOracle) ping(n *Node) error { func (t findnodeOracle) ping(toid NodeID, toaddr *net.UDPAddr) error { return nil }
return errors.New("ping is not supported by this transport")
}
func hasDuplicates(slice []*Node) bool { func hasDuplicates(slice []*Node) bool {
seen := make(map[NodeID]bool) seen := make(map[NodeID]bool)

View file

@ -16,11 +16,16 @@ import (
var log = logger.NewLogger("P2P Discovery") var log = logger.NewLogger("P2P Discovery")
const Version = 3
// Errors // Errors
var ( var (
errPacketTooSmall = errors.New("too small") errPacketTooSmall = errors.New("too small")
errBadHash = errors.New("bad hash") errBadHash = errors.New("bad hash")
errExpired = errors.New("expired") errExpired = errors.New("expired")
errBadVersion = errors.New("version mismatch")
errUnsolicitedReply = errors.New("unsolicited reply")
errUnknownNode = errors.New("unknown node")
errTimeout = errors.New("RPC timeout") errTimeout = errors.New("RPC timeout")
errClosed = errors.New("socket closed") errClosed = errors.New("socket closed")
) )
@ -45,6 +50,7 @@ const (
// RPC request structures // RPC request structures
type ( type (
ping struct { ping struct {
Version uint // must match Version
IP string // our IP IP string // our IP
Port uint16 // our port Port uint16 // our port
Expiration uint64 Expiration uint64
@ -76,12 +82,25 @@ type rpcNode struct {
ID NodeID ID NodeID
} }
type packet interface {
handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error
}
type conn interface {
ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error)
WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error)
Close() error
LocalAddr() net.Addr
}
// udp implements the RPC protocol. // udp implements the RPC protocol.
type udp struct { type udp struct {
conn *net.UDPConn conn conn
priv *ecdsa.PrivateKey priv *ecdsa.PrivateKey
addpending chan *pending addpending chan *pending
replies chan reply gotreply chan reply
closing chan struct{} closing chan struct{}
nat nat.Interface nat nat.Interface
@ -120,6 +139,9 @@ type reply struct {
from NodeID from NodeID
ptype byte ptype byte
data interface{} data interface{}
// loop indicates whether there was
// a matching request by sending on this channel.
matched chan<- bool
} }
// ListenUDP returns a new table that listens for UDP packets on laddr. // ListenUDP returns a new table that listens for UDP packets on laddr.
@ -132,15 +154,20 @@ func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface) (*Table
if err != nil { if err != nil {
return nil, err return nil, err
} }
tab, _ := newUDP(priv, conn, natm)
log.Infoln("Listening,", tab.self)
return tab, nil
}
func newUDP(priv *ecdsa.PrivateKey, c conn, natm nat.Interface) (*Table, *udp) {
udp := &udp{ udp := &udp{
conn: conn, conn: c,
priv: priv, priv: priv,
closing: make(chan struct{}), closing: make(chan struct{}),
gotreply: make(chan reply),
addpending: make(chan *pending), addpending: make(chan *pending),
replies: make(chan reply),
} }
realaddr := c.LocalAddr().(*net.UDPAddr)
realaddr := conn.LocalAddr().(*net.UDPAddr)
if natm != nil { if natm != nil {
if !realaddr.IP.IsLoopback() { if !realaddr.IP.IsLoopback() {
go nat.Map(natm, udp.closing, "udp", realaddr.Port, realaddr.Port, "ethereum discovery") go nat.Map(natm, udp.closing, "udp", realaddr.Port, realaddr.Port, "ethereum discovery")
@ -151,11 +178,9 @@ func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface) (*Table
} }
} }
udp.Table = newTable(udp, PubkeyID(&priv.PublicKey), realaddr) udp.Table = newTable(udp, PubkeyID(&priv.PublicKey), realaddr)
go udp.loop() go udp.loop()
go udp.readLoop() go udp.readLoop()
log.Infoln("Listening, ", udp.self) return udp.Table, udp
return udp.Table, nil
} }
func (t *udp) close() { func (t *udp) close() {
@ -165,10 +190,11 @@ func (t *udp) close() {
} }
// ping sends a ping message to the given node and waits for a reply. // ping sends a ping message to the given node and waits for a reply.
func (t *udp) ping(e *Node) error { func (t *udp) ping(toid NodeID, toaddr *net.UDPAddr) error {
// TODO: maybe check for ReplyTo field in callback to measure RTT // TODO: maybe check for ReplyTo field in callback to measure RTT
errc := t.pending(e.ID, pongPacket, func(interface{}) bool { return true }) errc := t.pending(toid, pongPacket, func(interface{}) bool { return true })
t.send(e, pingPacket, ping{ t.send(toaddr, pingPacket, ping{
Version: Version,
IP: t.self.IP.String(), IP: t.self.IP.String(),
Port: uint16(t.self.TCPPort), Port: uint16(t.self.TCPPort),
Expiration: uint64(time.Now().Add(expiration).Unix()), Expiration: uint64(time.Now().Add(expiration).Unix()),
@ -176,12 +202,16 @@ func (t *udp) ping(e *Node) error {
return <-errc return <-errc
} }
func (t *udp) waitping(from NodeID) error {
return <-t.pending(from, pingPacket, func(interface{}) bool { return true })
}
// findnode sends a findnode request to the given node and waits until // findnode sends a findnode request to the given node and waits until
// the node has sent up to k neighbors. // the node has sent up to k neighbors.
func (t *udp) findnode(to *Node, target NodeID) ([]*Node, error) { func (t *udp) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node, error) {
nodes := make([]*Node, 0, bucketSize) nodes := make([]*Node, 0, bucketSize)
nreceived := 0 nreceived := 0
errc := t.pending(to.ID, neighborsPacket, func(r interface{}) bool { errc := t.pending(toid, neighborsPacket, func(r interface{}) bool {
reply := r.(*neighbors) reply := r.(*neighbors)
for _, n := range reply.Nodes { for _, n := range reply.Nodes {
nreceived++ nreceived++
@ -191,8 +221,7 @@ func (t *udp) findnode(to *Node, target NodeID) ([]*Node, error) {
} }
return nreceived >= bucketSize return nreceived >= bucketSize
}) })
t.send(toaddr, findnodePacket, findnode{
t.send(to, findnodePacket, findnode{
Target: target, Target: target,
Expiration: uint64(time.Now().Add(expiration).Unix()), Expiration: uint64(time.Now().Add(expiration).Unix()),
}) })
@ -214,6 +243,17 @@ func (t *udp) pending(id NodeID, ptype byte, callback func(interface{}) bool) <-
return ch return ch
} }
func (t *udp) handleReply(from NodeID, ptype byte, req packet) bool {
matched := make(chan bool)
select {
case t.gotreply <- reply{from, ptype, req, matched}:
// loop will handle it
return <-matched
case <-t.closing:
return false
}
}
// loop runs in its own goroutin. it keeps track of // loop runs in its own goroutin. it keeps track of
// the refresh timer and the pending reply queue. // the refresh timer and the pending reply queue.
func (t *udp) loop() { func (t *udp) loop() {
@ -244,6 +284,7 @@ func (t *udp) loop() {
for _, p := range pending { for _, p := range pending {
p.errc <- errClosed p.errc <- errClosed
} }
pending = nil
return return
case p := <-t.addpending: case p := <-t.addpending:
@ -251,18 +292,21 @@ func (t *udp) loop() {
pending = append(pending, p) pending = append(pending, p)
rearmTimeout() rearmTimeout()
case reply := <-t.replies: case r := <-t.gotreply:
// run matching callbacks, remove if they return false. var matched bool
for i := 0; i < len(pending); i++ { for i := 0; i < len(pending); i++ {
p := pending[i] if p := pending[i]; p.from == r.from && p.ptype == r.ptype {
if reply.from == p.from && reply.ptype == p.ptype && p.callback(reply.data) { matched = true
if p.callback(r.data) {
// callback indicates the request is done, remove it.
p.errc <- nil p.errc <- nil
copy(pending[i:], pending[i+1:]) copy(pending[i:], pending[i+1:])
pending = pending[:len(pending)-1] pending = pending[:len(pending)-1]
i-- i--
} }
} }
rearmTimeout() }
r.matched <- matched
case now := <-timeout.C: case now := <-timeout.C:
// notify and remove callbacks whose deadline is in the past. // notify and remove callbacks whose deadline is in the past.
@ -287,28 +331,11 @@ const (
var headSpace = make([]byte, headSize) var headSpace = make([]byte, headSize)
func (t *udp) send(to *Node, ptype byte, req interface{}) error { func (t *udp) send(toaddr *net.UDPAddr, ptype byte, req interface{}) error {
b := new(bytes.Buffer) packet, err := encodePacket(t.priv, ptype, req)
b.Write(headSpace)
b.WriteByte(ptype)
if err := rlp.Encode(b, req); err != nil {
log.Errorln("error encoding packet:", err)
return err
}
packet := b.Bytes()
sig, err := crypto.Sign(crypto.Sha3(packet[headSize:]), t.priv)
if err != nil { if err != nil {
log.Errorln("could not sign packet:", err)
return err return err
} }
copy(packet[macSize:], sig)
// add the hash to the front. Note: this doesn't protect the
// packet in any way. Our public key will be part of this hash in
// the future.
copy(packet, crypto.Sha3(packet[macSize:]))
toaddr := &net.UDPAddr{IP: to.IP, Port: to.DiscPort}
log.DebugDetailf(">>> %v %T %v\n", toaddr, req, req) log.DebugDetailf(">>> %v %T %v\n", toaddr, req, req)
if _, err = t.conn.WriteToUDP(packet, toaddr); err != nil { if _, err = t.conn.WriteToUDP(packet, toaddr); err != nil {
log.DebugDetailln("UDP send failed:", err) log.DebugDetailln("UDP send failed:", err)
@ -316,6 +343,28 @@ func (t *udp) send(to *Node, ptype byte, req interface{}) error {
return err return err
} }
func encodePacket(priv *ecdsa.PrivateKey, ptype byte, req interface{}) ([]byte, error) {
b := new(bytes.Buffer)
b.Write(headSpace)
b.WriteByte(ptype)
if err := rlp.Encode(b, req); err != nil {
log.Errorln("error encoding packet:", err)
return nil, err
}
packet := b.Bytes()
sig, err := crypto.Sign(crypto.Sha3(packet[headSize:]), priv)
if err != nil {
log.Errorln("could not sign packet:", err)
return nil, err
}
copy(packet[macSize:], sig)
// add the hash to the front. Note: this doesn't protect the
// packet in any way. Our public key will be part of this hash in
// The future.
copy(packet, crypto.Sha3(packet[macSize:]))
return packet, nil
}
// readLoop runs in its own goroutine. it handles incoming UDP packets. // readLoop runs in its own goroutine. it handles incoming UDP packets.
func (t *udp) readLoop() { func (t *udp) readLoop() {
defer t.conn.Close() defer t.conn.Close()
@ -325,29 +374,34 @@ func (t *udp) readLoop() {
if err != nil { if err != nil {
return return
} }
if err := t.packetIn(from, buf[:nbytes]); err != nil { packet, fromID, hash, err := decodePacket(buf[:nbytes])
if err != nil {
log.Debugf("Bad packet from %v: %v\n", from, err) log.Debugf("Bad packet from %v: %v\n", from, err)
continue
} }
log.DebugDetailf("<<< %v %T %v\n", from, packet, packet)
go func() {
if err := packet.handle(t, from, fromID, hash); err != nil {
log.Debugf("error handling %T from %v: %v", packet, from, err)
}
}()
} }
} }
func (t *udp) packetIn(from *net.UDPAddr, buf []byte) error { func decodePacket(buf []byte) (packet, NodeID, []byte, error) {
if len(buf) < headSize+1 { if len(buf) < headSize+1 {
return errPacketTooSmall return nil, NodeID{}, nil, errPacketTooSmall
} }
hash, sig, sigdata := buf[:macSize], buf[macSize:headSize], buf[headSize:] hash, sig, sigdata := buf[:macSize], buf[macSize:headSize], buf[headSize:]
shouldhash := crypto.Sha3(buf[macSize:]) shouldhash := crypto.Sha3(buf[macSize:])
if !bytes.Equal(hash, shouldhash) { if !bytes.Equal(hash, shouldhash) {
return errBadHash return nil, NodeID{}, nil, errBadHash
} }
fromID, err := recoverNodeID(crypto.Sha3(buf[headSize:]), sig) fromID, err := recoverNodeID(crypto.Sha3(buf[headSize:]), sig)
if err != nil { if err != nil {
return err return nil, NodeID{}, hash, err
}
var req interface {
handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error
} }
var req packet
switch ptype := sigdata[0]; ptype { switch ptype := sigdata[0]; ptype {
case pingPacket: case pingPacket:
req = new(ping) req = new(ping)
@ -358,31 +412,27 @@ func (t *udp) packetIn(from *net.UDPAddr, buf []byte) error {
case neighborsPacket: case neighborsPacket:
req = new(neighbors) req = new(neighbors)
default: default:
return fmt.Errorf("unknown type: %d", ptype) return nil, fromID, hash, fmt.Errorf("unknown type: %d", ptype)
} }
if err := rlp.Decode(bytes.NewReader(sigdata[1:]), req); err != nil { err = rlp.Decode(bytes.NewReader(sigdata[1:]), req)
return err return req, fromID, hash, err
}
log.DebugDetailf("<<< %v %T %v\n", from, req, req)
return req.handle(t, from, fromID, hash)
} }
func (req *ping) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error { func (req *ping) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) error {
if expired(req.Expiration) { if expired(req.Expiration) {
return errExpired return errExpired
} }
t.mutex.Lock() if req.Version != Version {
// Note: we're ignoring the provided IP address right now return errBadVersion
n := t.bumpOrAdd(fromID, from)
if req.Port != 0 {
n.TCPPort = int(req.Port)
} }
t.mutex.Unlock() t.send(from, pongPacket, pong{
t.send(n, pongPacket, pong{
ReplyTok: mac, ReplyTok: mac,
Expiration: uint64(time.Now().Add(expiration).Unix()), Expiration: uint64(time.Now().Add(expiration).Unix()),
}) })
if !t.handleReply(fromID, pingPacket, req) {
// Note: we're ignoring the provided IP address right now
t.bond(true, fromID, from, req.Port)
}
return nil return nil
} }
@ -390,11 +440,9 @@ func (req *pong) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte) er
if expired(req.Expiration) { if expired(req.Expiration) {
return errExpired return errExpired
} }
t.mutex.Lock() if !t.handleReply(fromID, pongPacket, req) {
t.bump(fromID) return errUnsolicitedReply
t.mutex.Unlock() }
t.replies <- reply{fromID, pongPacket, req}
return nil return nil
} }
@ -402,12 +450,21 @@ func (req *findnode) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte
if expired(req.Expiration) { if expired(req.Expiration) {
return errExpired return errExpired
} }
if t.db.get(fromID) == nil {
// No bond exists, we don't process the packet. This prevents
// an attack vector where the discovery protocol could be used
// to amplify traffic in a DDOS attack. A malicious actor
// would send a findnode request with the IP address and UDP
// port of the target as the source address. The recipient of
// the findnode packet would then send a neighbors packet
// (which is a much bigger packet than findnode) to the victim.
return errUnknownNode
}
t.mutex.Lock() t.mutex.Lock()
e := t.bumpOrAdd(fromID, from)
closest := t.closest(req.Target, bucketSize).entries closest := t.closest(req.Target, bucketSize).entries
t.mutex.Unlock() t.mutex.Unlock()
t.send(e, neighborsPacket, neighbors{ t.send(from, neighborsPacket, neighbors{
Nodes: closest, Nodes: closest,
Expiration: uint64(time.Now().Add(expiration).Unix()), Expiration: uint64(time.Now().Add(expiration).Unix()),
}) })
@ -418,12 +475,9 @@ func (req *neighbors) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byt
if expired(req.Expiration) { if expired(req.Expiration) {
return errExpired return errExpired
} }
t.mutex.Lock() if !t.handleReply(fromID, neighborsPacket, req) {
t.bump(fromID) return errUnsolicitedReply
t.add(req.Nodes) }
t.mutex.Unlock()
t.replies <- reply{fromID, neighborsPacket, req}
return nil return nil
} }

View file

@ -1,10 +1,18 @@
package discover package discover
import ( import (
"bytes"
"crypto/ecdsa"
"errors"
"fmt" "fmt"
"io"
logpkg "log" logpkg "log"
"net" "net"
"os" "os"
"path"
"reflect"
"runtime"
"sync"
"testing" "testing"
"time" "time"
@ -15,22 +23,243 @@ func init() {
logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, logpkg.LstdFlags, logger.ErrorLevel)) logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, logpkg.LstdFlags, logger.ErrorLevel))
} }
func TestUDP_ping(t *testing.T) { type udpTest struct {
t *testing.T
pipe *dgramPipe
table *Table
udp *udp
sent [][]byte
localkey, remotekey *ecdsa.PrivateKey
remoteaddr *net.UDPAddr
}
func newUDPTest(t *testing.T) *udpTest {
test := &udpTest{
t: t,
pipe: newpipe(),
localkey: newkey(),
remotekey: newkey(),
remoteaddr: &net.UDPAddr{IP: net.IP{1, 2, 3, 4}, Port: 30303},
}
test.table, test.udp = newUDP(test.localkey, test.pipe, nil)
return test
}
// handles a packet as if it had been sent to the transport.
func (test *udpTest) packetIn(wantError error, ptype byte, data packet) error {
enc, err := encodePacket(test.remotekey, ptype, data)
if err != nil {
return test.errorf("packet (%d) encode error: %v", err)
}
test.sent = append(test.sent, enc)
err = data.handle(test.udp, test.remoteaddr, PubkeyID(&test.remotekey.PublicKey), enc[:macSize])
if err != wantError {
return test.errorf("error mismatch: got %q, want %q", err, wantError)
}
return nil
}
// waits for a packet to be sent by the transport.
// validate should have type func(*udpTest, X) error, where X is a packet type.
func (test *udpTest) waitPacketOut(validate interface{}) error {
dgram := test.pipe.waitPacketOut()
p, _, _, err := decodePacket(dgram)
if err != nil {
return test.errorf("sent packet decode error: %v", err)
}
fn := reflect.ValueOf(validate)
exptype := fn.Type().In(0)
if reflect.TypeOf(p) != exptype {
return test.errorf("sent packet type mismatch, got: %v, want: %v", reflect.TypeOf(p), exptype)
}
fn.Call([]reflect.Value{reflect.ValueOf(p)})
return nil
}
func (test *udpTest) errorf(format string, args ...interface{}) error {
_, file, line, ok := runtime.Caller(2) // errorf + waitPacketOut
if ok {
file = path.Base(file)
} else {
file = "???"
line = 1
}
err := fmt.Errorf(format, args...)
fmt.Printf("\t%s:%d: %v\n", file, line, err)
test.t.Fail()
return err
}
// shared test variables
var (
futureExp = uint64(time.Now().Add(10 * time.Hour).Unix())
testTarget = MustHexID("01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101")
)
func TestUDP_packetErrors(t *testing.T) {
test := newUDPTest(t)
defer test.table.Close()
test.packetIn(errExpired, pingPacket, &ping{IP: "foo", Port: 99, Version: Version})
test.packetIn(errBadVersion, pingPacket, &ping{IP: "foo", Port: 99, Version: 99, Expiration: futureExp})
test.packetIn(errUnsolicitedReply, pongPacket, &pong{ReplyTok: []byte{}, Expiration: futureExp})
test.packetIn(errUnknownNode, findnodePacket, &findnode{Expiration: futureExp})
test.packetIn(errUnsolicitedReply, neighborsPacket, &neighbors{Expiration: futureExp})
}
func TestUDP_pingTimeout(t *testing.T) {
t.Parallel() t.Parallel()
test := newUDPTest(t)
defer test.table.Close()
n1, _ := ListenUDP(newkey(), "127.0.0.1:0", nil) toaddr := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 2222}
n2, _ := ListenUDP(newkey(), "127.0.0.1:0", nil) toid := NodeID{1, 2, 3, 4}
defer n1.Close() if err := test.udp.ping(toid, toaddr); err != errTimeout {
defer n2.Close() t.Error("expected timeout error, got", err)
}
}
if err := n1.net.ping(n2.self); err != nil { func TestUDP_findnodeTimeout(t *testing.T) {
t.Fatalf("ping error: %v", err) t.Parallel()
test := newUDPTest(t)
defer test.table.Close()
toaddr := &net.UDPAddr{IP: net.ParseIP("1.2.3.4"), Port: 2222}
toid := NodeID{1, 2, 3, 4}
target := NodeID{4, 5, 6, 7}
result, err := test.udp.findnode(toid, toaddr, target)
if err != errTimeout {
t.Error("expected timeout error, got", err)
} }
if find(n2, n1.self.ID) == nil { if len(result) > 0 {
t.Errorf("node 2 does not contain id of node 1") t.Error("expected empty result, got", result)
} }
if e := find(n1, n2.self.ID); e != nil { }
t.Errorf("node 1 does contains id of node 2: %v", e)
func TestUDP_findnode(t *testing.T) {
test := newUDPTest(t)
defer test.table.Close()
// put a few nodes into the table. their exact
// distribution shouldn't matter much, altough we need to
// take care not to overflow any bucket.
target := testTarget
nodes := &nodesByDistance{target: target}
for i := 0; i < bucketSize; i++ {
nodes.push(&Node{
IP: net.IP{1, 2, 3, byte(i)},
DiscPort: i + 2,
TCPPort: i + 2,
ID: randomID(test.table.self.ID, i+2),
}, bucketSize)
}
test.table.add(nodes.entries)
// ensure there's a bond with the test node,
// findnode won't be accepted otherwise.
test.table.db.add(PubkeyID(&test.remotekey.PublicKey), test.remoteaddr, 99)
// check that closest neighbors are returned.
test.packetIn(nil, findnodePacket, &findnode{Target: testTarget, Expiration: futureExp})
test.waitPacketOut(func(p *neighbors) {
expected := test.table.closest(testTarget, bucketSize)
if len(p.Nodes) != bucketSize {
t.Errorf("wrong number of results: got %d, want %d", len(p.Nodes), bucketSize)
}
for i := range p.Nodes {
if p.Nodes[i].ID != expected.entries[i].ID {
t.Errorf("result mismatch at %d:\n got: %v\n want: %v", i, p.Nodes[i], expected.entries[i])
}
}
})
}
func TestUDP_findnodeMultiReply(t *testing.T) {
test := newUDPTest(t)
defer test.table.Close()
// queue a pending findnode request
resultc, errc := make(chan []*Node), make(chan error)
go func() {
rid := PubkeyID(&test.remotekey.PublicKey)
ns, err := test.udp.findnode(rid, test.remoteaddr, testTarget)
if err != nil && len(ns) == 0 {
errc <- err
} else {
resultc <- ns
}
}()
// wait for the findnode to be sent.
// after it is sent, the transport is waiting for a reply
test.waitPacketOut(func(p *findnode) {
if p.Target != testTarget {
t.Errorf("wrong target: got %v, want %v", p.Target, testTarget)
}
})
// send the reply as two packets.
list := []*Node{
MustParseNode("enode://ba85011c70bcc5c04d8607d3a0ed29aa6179c092cbdda10d5d32684fb33ed01bd94f588ca8f91ac48318087dcb02eaf36773a7a453f0eedd6742af668097b29c@10.0.1.16:30303"),
MustParseNode("enode://81fa361d25f157cd421c60dcc28d8dac5ef6a89476633339c5df30287474520caca09627da18543d9079b5b288698b542d56167aa5c09111e55acdbbdf2ef799@10.0.1.16:30303"),
MustParseNode("enode://9bffefd833d53fac8e652415f4973bee289e8b1a5c6c4cbe70abf817ce8a64cee11b823b66a987f51aaa9fba0d6a91b3e6bf0d5a5d1042de8e9eeea057b217f8@10.0.1.36:30301"),
MustParseNode("enode://1b5b4aa662d7cb44a7221bfba67302590b643028197a7d5214790f3bac7aaa4a3241be9e83c09cf1f6c69d007c634faae3dc1b1221793e8446c0b3a09de65960@10.0.1.16:30303"),
}
test.packetIn(nil, neighborsPacket, &neighbors{Expiration: futureExp, Nodes: list[:2]})
test.packetIn(nil, neighborsPacket, &neighbors{Expiration: futureExp, Nodes: list[2:]})
// check that the sent neighbors are all returned by findnode
select {
case result := <-resultc:
if !reflect.DeepEqual(result, list) {
t.Errorf("neighbors mismatch:\n got: %v\n want: %v", result, list)
}
case err := <-errc:
t.Errorf("findnode error: %v", err)
case <-time.After(5 * time.Second):
t.Error("findnode did not return within 5 seconds")
}
}
func TestUDP_successfulPing(t *testing.T) {
test := newUDPTest(t)
defer test.table.Close()
done := make(chan struct{})
go func() {
test.packetIn(nil, pingPacket, &ping{IP: "foo", Port: 99, Version: Version, Expiration: futureExp})
close(done)
}()
// the ping is replied to.
test.waitPacketOut(func(p *pong) {
pinghash := test.sent[0][:macSize]
if !bytes.Equal(p.ReplyTok, pinghash) {
t.Errorf("got ReplyTok %x, want %x", p.ReplyTok, pinghash)
}
})
// remote is unknown, the table pings back.
test.waitPacketOut(func(p *ping) error { return nil })
test.packetIn(nil, pongPacket, &pong{Expiration: futureExp})
// ping should return shortly after getting the pong packet.
<-done
// check that the node was added.
rid := PubkeyID(&test.remotekey.PublicKey)
rnode := find(test.table, rid)
if rnode == nil {
t.Fatalf("node %v not found in table", rid)
}
if !bytes.Equal(rnode.IP, test.remoteaddr.IP) {
t.Errorf("node has wrong IP: got %v, want: %v", rnode.IP, test.remoteaddr.IP)
}
if rnode.DiscPort != test.remoteaddr.Port {
t.Errorf("node has wrong Port: got %v, want: %v", rnode.DiscPort, test.remoteaddr.Port)
}
if rnode.TCPPort != 99 {
t.Errorf("node has wrong Port: got %v, want: %v", rnode.TCPPort, 99)
} }
} }
@ -45,167 +274,66 @@ func find(tab *Table, id NodeID) *Node {
return nil return nil
} }
func TestUDP_findnode(t *testing.T) { // dgramPipe is a fake UDP socket. It queues all sent datagrams.
t.Parallel() type dgramPipe struct {
mu *sync.Mutex
cond *sync.Cond
closing chan struct{}
closed bool
queue [][]byte
}
n1, _ := ListenUDP(newkey(), "127.0.0.1:0", nil) func newpipe() *dgramPipe {
n2, _ := ListenUDP(newkey(), "127.0.0.1:0", nil) mu := new(sync.Mutex)
defer n1.Close() return &dgramPipe{
defer n2.Close() closing: make(chan struct{}),
cond: &sync.Cond{L: mu},
// put a few nodes into n2. the exact distribution shouldn't mu: mu,
// matter much, altough we need to take care not to overflow
// any bucket.
target := randomID(n1.self.ID, 100)
nodes := &nodesByDistance{target: target}
for i := 0; i < bucketSize; i++ {
n2.add([]*Node{&Node{
IP: net.IP{1, 2, 3, byte(i)},
DiscPort: i + 2,
TCPPort: i + 2,
ID: randomID(n2.self.ID, i+2),
}})
}
n2.add(nodes.entries)
n2.bumpOrAdd(n1.self.ID, &net.UDPAddr{IP: n1.self.IP, Port: n1.self.DiscPort})
expected := n2.closest(target, bucketSize)
err := runUDP(10, func() error {
result, _ := n1.net.findnode(n2.self, target)
if len(result) != bucketSize {
return fmt.Errorf("wrong number of results: got %d, want %d", len(result), bucketSize)
}
for i := range result {
if result[i].ID != expected.entries[i].ID {
return fmt.Errorf("result mismatch at %d:\n got: %v\n want: %v", i, result[i], expected.entries[i])
}
}
return nil
})
if err != nil {
t.Error(err)
} }
} }
func TestUDP_replytimeout(t *testing.T) { // WriteToUDP queues a datagram.
t.Parallel() func (c *dgramPipe) WriteToUDP(b []byte, to *net.UDPAddr) (n int, err error) {
msg := make([]byte, len(b))
// reserve a port so we don't talk to an existing service by accident copy(msg, b)
addr, _ := net.ResolveUDPAddr("udp", "127.0.0.1:0") c.mu.Lock()
fd, err := net.ListenUDP("udp", addr) defer c.mu.Unlock()
if err != nil { if c.closed {
t.Fatal(err) return 0, errors.New("closed")
}
defer fd.Close()
n1, _ := ListenUDP(newkey(), "127.0.0.1:0", nil)
defer n1.Close()
n2 := n1.bumpOrAdd(randomID(n1.self.ID, 10), fd.LocalAddr().(*net.UDPAddr))
if err := n1.net.ping(n2); err != errTimeout {
t.Error("expected timeout error, got", err)
}
if result, err := n1.net.findnode(n2, n1.self.ID); err != errTimeout {
t.Error("expected timeout error, got", err)
} else if len(result) > 0 {
t.Error("expected empty result, got", result)
} }
c.queue = append(c.queue, msg)
c.cond.Signal()
return len(b), nil
} }
func TestUDP_findnodeMultiReply(t *testing.T) { // ReadFromUDP just hangs until the pipe is closed.
t.Parallel() func (c *dgramPipe) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) {
<-c.closing
n1, _ := ListenUDP(newkey(), "127.0.0.1:0", nil) return 0, nil, io.EOF
n2, _ := ListenUDP(newkey(), "127.0.0.1:0", nil)
udp2 := n2.net.(*udp)
defer n1.Close()
defer n2.Close()
err := runUDP(10, func() error {
nodes := make([]*Node, bucketSize)
for i := range nodes {
nodes[i] = &Node{
IP: net.IP{1, 2, 3, 4},
DiscPort: i + 1,
TCPPort: i + 1,
ID: randomID(n2.self.ID, i+1),
}
}
// ask N2 for neighbors. it will send an empty reply back.
// the request will wait for up to bucketSize replies.
resultc := make(chan []*Node)
errc := make(chan error)
go func() {
ns, err := n1.net.findnode(n2.self, n1.self.ID)
if err != nil {
errc <- err
} else {
resultc <- ns
}
}()
// send a few more neighbors packets to N1.
// it should collect those.
for end := 0; end < len(nodes); {
off := end
if end = end + 5; end > len(nodes) {
end = len(nodes)
}
udp2.send(n1.self, neighborsPacket, neighbors{
Nodes: nodes[off:end],
Expiration: uint64(time.Now().Add(10 * time.Second).Unix()),
})
}
// check that they are all returned. we cannot just check for
// equality because they might not be returned in the order they
// were sent.
var result []*Node
select {
case result = <-resultc:
case err := <-errc:
return err
}
if hasDuplicates(result) {
return fmt.Errorf("result slice contains duplicates")
}
if len(result) != len(nodes) {
return fmt.Errorf("wrong number of nodes returned: got %d, want %d", len(result), len(nodes))
}
matched := make(map[NodeID]bool)
for _, n := range result {
for _, expn := range nodes {
if n.ID == expn.ID { // && bytes.Equal(n.Addr.IP, expn.Addr.IP) && n.Addr.Port == expn.Addr.Port {
matched[n.ID] = true
}
}
}
if len(matched) != len(nodes) {
return fmt.Errorf("wrong number of matching nodes: got %d, want %d", len(matched), len(nodes))
}
return nil
})
if err != nil {
t.Error(err)
}
} }
// runUDP runs a test n times and returns an error if the test failed func (c *dgramPipe) Close() error {
// in all n runs. This is necessary because UDP is unreliable even for c.mu.Lock()
// connections on the local machine, causing test failures. defer c.mu.Unlock()
func runUDP(n int, test func() error) error { if !c.closed {
errcount := 0 close(c.closing)
errors := "" c.closed = true
for i := 0; i < n; i++ {
if err := test(); err != nil {
errors += fmt.Sprintf("\n#%d: %v", i, err)
errcount++
}
}
if errcount == n {
return fmt.Errorf("failed on all %d iterations:%s", n, errors)
} }
return nil return nil
} }
func (c *dgramPipe) LocalAddr() net.Addr {
return &net.UDPAddr{}
}
func (c *dgramPipe) waitPacketOut() []byte {
c.mu.Lock()
defer c.mu.Unlock()
for len(c.queue) == 0 {
c.cond.Wait()
}
p := c.queue[0]
copy(c.queue, c.queue[1:])
c.queue = c.queue[:len(c.queue)-1]
return p
}

54
params/protocol_params.go Executable file
View file

@ -0,0 +1,54 @@
// DO NOT EDIT!!!
// AUTOGENERATED FROM generators/defaults.go
package params
import "math/big"
var (
MaximumExtraDataSize = big.NewInt(1024) // Maximum size extra data may be after Genesis.
ExpByteGas = big.NewInt(10) // Times ceil(log256(exponent)) for the EXP instruction.
SloadGas = big.NewInt(50) // Multiplied by the number of 32-byte words that are copied (round up) for any *COPY operation and added.
CallValueTransferGas = big.NewInt(9000) // Paid for CALL when the value transfor is non-zero.
CallNewAccountGas = big.NewInt(25000) // Paid for CALL when the destination address didn't exist prior.
TxGas = big.NewInt(21000) // Per transaction. NOTE: Not payable on data of calls between transactions.
TxDataZeroGas = big.NewInt(4) // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions.
GenesisGasLimit = big.NewInt(3141592) // Gas limit of the Genesis block.
DifficultyBoundDivisor = big.NewInt(2048) // The bound divisor of the difficulty, used in the update calculations.
QuadCoeffDiv = big.NewInt(512) // Divisor for the quadratic particle of the memory cost equation.
GenesisDifficulty = big.NewInt(131072) // Difficulty of the Genesis block.
DurationLimit = big.NewInt(8) // The decision boundary on the blocktime duration used to determine whether difficulty should go up or not.
SstoreSetGas = big.NewInt(20000) // Once per SLOAD operation.
LogDataGas = big.NewInt(8) // Per byte in a LOG* operation's data.
CallStipend = big.NewInt(2300) // Free gas given at beginning of call.
EcrecoverGas = big.NewInt(3000) //
Sha256WordGas = big.NewInt(12) //
MinGasLimit = big.NewInt(125000) // Minimum the gas limit may ever be.
Sha3Gas = big.NewInt(30) // Once per SHA3 operation.
Sha256Gas = big.NewInt(60) //
IdentityWordGas = big.NewInt(3) //
Sha3WordGas = big.NewInt(6) // Once per word of the SHA3 operation's data.
SstoreResetGas = big.NewInt(5000) // Once per SSTORE operation if the zeroness changes from zero.
SstoreClearGas = big.NewInt(5000) // Once per SSTORE operation if the zeroness doesn't change.
SstoreRefundGas = big.NewInt(15000) // Once per SSTORE operation if the zeroness changes to zero.
JumpdestGas = big.NewInt(1) // Refunded gas, once per SSTORE operation if the zeroness changes to zero.
IdentityGas = big.NewInt(15) //
GasLimitBoundDivisor = big.NewInt(1024) // The bound divisor of the gas limit, used in update calculations.
EpochDuration = big.NewInt(30000) // Duration between proof-of-work epochs.
CallGas = big.NewInt(40) // Once per CALL operation & message call transaction.
CreateDataGas = big.NewInt(200) //
Ripemd160Gas = big.NewInt(600) //
Ripemd160WordGas = big.NewInt(120) //
MinimumDifficulty = big.NewInt(131072) // The minimum that the difficulty may ever be.
CallCreateDepth = big.NewInt(1024) // Maximum depth of call/create stack.
ExpGas = big.NewInt(10) // Once per EXP instuction.
LogGas = big.NewInt(375) // Per LOG* operation.
CopyGas = big.NewInt(3) //
StackLimit = big.NewInt(1024) // Maximum size of VM stack allowed.
TierStepGas = big.NewInt(0) // Once per operation, for a selection of them.
LogTopicGas = big.NewInt(375) // Multiplied by the * of the LOG*, per LOG transaction. e.g. LOG0 incurs 0 * c_txLogTopicGas, LOG4 incurs 4 * c_txLogTopicGas.
CreateGas = big.NewInt(32000) // Once per CREATE operation & contract-creation transaction.
SuicideRefundGas = big.NewInt(24000) // Refunded following a suicide operation.
MemoryGas = big.NewInt(3) // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.
TxDataNonZeroGas = big.NewInt(68) // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions.
)

View file

@ -3,31 +3,21 @@ package rpc
import ( import (
"encoding/json" "encoding/json"
"math/big" "math/big"
"path"
"sync" "sync"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/xeth" "github.com/ethereum/go-ethereum/xeth"
) )
type EthereumApi struct { type EthereumApi struct {
eth *xeth.XEth eth *xeth.XEth
xethMu sync.RWMutex xethMu sync.RWMutex
db common.Database
} }
func NewEthereumApi(xeth *xeth.XEth, dataDir string) *EthereumApi { func NewEthereumApi(xeth *xeth.XEth) *EthereumApi {
// What about when dataDir is empty?
db, err := ethdb.NewLDBDatabase(path.Join(dataDir, "dapps"))
if err != nil {
panic(err)
}
api := &EthereumApi{ api := &EthereumApi{
eth: xeth, eth: xeth,
db: db,
} }
return api return api
@ -44,10 +34,6 @@ func (api *EthereumApi) xethAtStateNum(num int64) *xeth.XEth {
return api.xeth().AtStateNum(num) return api.xeth().AtStateNum(num)
} }
func (api *EthereumApi) Close() {
api.db.Close()
}
func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error { func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) error {
// Spec at https://github.com/ethereum/wiki/wiki/JSON-RPC // Spec at https://github.com/ethereum/wiki/wiki/JSON-RPC
rpclogger.Debugf("%s %s", req.Method, req.Params) rpclogger.Debugf("%s %s", req.Method, req.Params)
@ -68,7 +54,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
case "net_peerCount": case "net_peerCount":
v := api.xeth().PeerCount() v := api.xeth().PeerCount()
*reply = common.ToHex(big.NewInt(int64(v)).Bytes()) *reply = common.ToHex(big.NewInt(int64(v)).Bytes())
case "eth_version": case "eth_protocolVersion":
*reply = api.xeth().EthVersion() *reply = api.xeth().EthVersion()
case "eth_coinbase": case "eth_coinbase":
// TODO handling of empty coinbase due to lack of accounts // TODO handling of empty coinbase due to lack of accounts
@ -94,10 +80,6 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
return err return err
} }
if err := args.requirements(); err != nil {
return err
}
v := api.xethAtStateNum(args.BlockNumber).State().SafeGet(args.Address).Balance() v := api.xethAtStateNum(args.BlockNumber).State().SafeGet(args.Address).Balance()
*reply = common.ToHex(v.Bytes()) *reply = common.ToHex(v.Bytes())
case "eth_getStorage", "eth_storageAt": case "eth_getStorage", "eth_storageAt":
@ -106,43 +88,29 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
return err return err
} }
if err := args.requirements(); err != nil {
return err
}
*reply = api.xethAtStateNum(args.BlockNumber).State().SafeGet(args.Address).Storage() *reply = api.xethAtStateNum(args.BlockNumber).State().SafeGet(args.Address).Storage()
case "eth_getStorageAt": case "eth_getStorageAt":
args := new(GetStorageAtArgs) args := new(GetStorageAtArgs)
if err := json.Unmarshal(req.Params, &args); err != nil { if err := json.Unmarshal(req.Params, &args); err != nil {
return err return err
} }
if err := args.requirements(); err != nil {
return err
}
state := api.xethAtStateNum(args.BlockNumber).State().SafeGet(args.Address) *reply = api.xethAtStateNum(args.BlockNumber).StorageAt(args.Address, args.Key)
value := state.StorageString(args.Key)
*reply = common.Bytes2Hex(value.Bytes())
case "eth_getTransactionCount": case "eth_getTransactionCount":
args := new(GetTxCountArgs) args := new(GetTxCountArgs)
if err := json.Unmarshal(req.Params, &args); err != nil { if err := json.Unmarshal(req.Params, &args); err != nil {
return err return err
} }
err := args.requirements() count := api.xethAtStateNum(args.BlockNumber).TxCountAt(args.Address)
if err != nil { *reply = common.ToHex(big.NewInt(int64(count)).Bytes())
return err
}
*reply = api.xethAtStateNum(args.BlockNumber).TxCountAt(args.Address)
case "eth_getBlockTransactionCountByHash": case "eth_getBlockTransactionCountByHash":
args := new(GetBlockByHashArgs) args := new(GetBlockByHashArgs)
if err := json.Unmarshal(req.Params, &args); err != nil { if err := json.Unmarshal(req.Params, &args); err != nil {
return err return err
} }
block := NewBlockRes(api.xeth().EthBlockByHash(args.BlockHash)) block := NewBlockRes(api.xeth().EthBlockByHash(args.BlockHash), false)
*reply = common.ToHex(big.NewInt(int64(len(block.Transactions))).Bytes()) *reply = common.ToHex(big.NewInt(int64(len(block.Transactions))).Bytes())
case "eth_getBlockTransactionCountByNumber": case "eth_getBlockTransactionCountByNumber":
args := new(GetBlockByNumberArgs) args := new(GetBlockByNumberArgs)
@ -150,7 +118,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
return err return err
} }
block := NewBlockRes(api.xeth().EthBlockByNumber(args.BlockNumber)) block := NewBlockRes(api.xeth().EthBlockByNumber(args.BlockNumber), false)
*reply = common.ToHex(big.NewInt(int64(len(block.Transactions))).Bytes()) *reply = common.ToHex(big.NewInt(int64(len(block.Transactions))).Bytes())
case "eth_getUncleCountByBlockHash": case "eth_getUncleCountByBlockHash":
args := new(GetBlockByHashArgs) args := new(GetBlockByHashArgs)
@ -159,7 +127,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
} }
block := api.xeth().EthBlockByHash(args.BlockHash) block := api.xeth().EthBlockByHash(args.BlockHash)
br := NewBlockRes(block) br := NewBlockRes(block, false)
*reply = common.ToHex(big.NewInt(int64(len(br.Uncles))).Bytes()) *reply = common.ToHex(big.NewInt(int64(len(br.Uncles))).Bytes())
case "eth_getUncleCountByBlockNumber": case "eth_getUncleCountByBlockNumber":
args := new(GetBlockByNumberArgs) args := new(GetBlockByNumberArgs)
@ -168,16 +136,13 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
} }
block := api.xeth().EthBlockByNumber(args.BlockNumber) block := api.xeth().EthBlockByNumber(args.BlockNumber)
br := NewBlockRes(block) br := NewBlockRes(block, false)
*reply = common.ToHex(big.NewInt(int64(len(br.Uncles))).Bytes()) *reply = common.ToHex(big.NewInt(int64(len(br.Uncles))).Bytes())
case "eth_getData", "eth_getCode": case "eth_getData", "eth_getCode":
args := new(GetDataArgs) args := new(GetDataArgs)
if err := json.Unmarshal(req.Params, &args); err != nil { if err := json.Unmarshal(req.Params, &args); err != nil {
return err return err
} }
if err := args.requirements(); err != nil {
return err
}
*reply = api.xethAtStateNum(args.BlockNumber).CodeAt(args.Address) *reply = api.xethAtStateNum(args.BlockNumber).CodeAt(args.Address)
case "eth_sendTransaction", "eth_transact": case "eth_sendTransaction", "eth_transact":
args := new(NewTxArgs) args := new(NewTxArgs)
@ -185,17 +150,13 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
return err return err
} }
if err := args.requirements(); err != nil {
return err
}
v, err := api.xeth().Transact(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data) v, err := api.xeth().Transact(args.From, args.To, args.Value.String(), args.Gas.String(), args.GasPrice.String(), args.Data)
if err != nil { if err != nil {
return err return err
} }
*reply = v *reply = v
case "eth_call": case "eth_call":
args := new(NewTxArgs) args := new(CallArgs)
if err := json.Unmarshal(req.Params, &args); err != nil { if err := json.Unmarshal(req.Params, &args); err != nil {
return err return err
} }
@ -215,8 +176,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
} }
block := api.xeth().EthBlockByHash(args.BlockHash) block := api.xeth().EthBlockByHash(args.BlockHash)
br := NewBlockRes(block) br := NewBlockRes(block, true)
br.fullTx = args.IncludeTxs
*reply = br *reply = br
case "eth_getBlockByNumber": case "eth_getBlockByNumber":
@ -226,8 +186,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
} }
block := api.xeth().EthBlockByNumber(args.BlockNumber) block := api.xeth().EthBlockByNumber(args.BlockNumber)
br := NewBlockRes(block) br := NewBlockRes(block, true)
br.fullTx = args.IncludeTxs
*reply = br *reply = br
case "eth_getTransactionByHash": case "eth_getTransactionByHash":
@ -235,9 +194,13 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
args := new(HashIndexArgs) args := new(HashIndexArgs)
if err := json.Unmarshal(req.Params, &args); err != nil { if err := json.Unmarshal(req.Params, &args); err != nil {
} }
tx := api.xeth().EthTransactionByHash(args.Hash) tx, bhash, bnum, txi := api.xeth().EthTransactionByHash(args.Hash)
if tx != nil { if tx != nil {
*reply = NewTransactionRes(tx) v := NewTransactionRes(tx)
v.BlockHash = newHexData(bhash)
v.BlockNumber = newHexNum(bnum)
v.TxIndex = newHexNum(txi)
*reply = v
} }
case "eth_getTransactionByBlockHashAndIndex": case "eth_getTransactionByBlockHashAndIndex":
args := new(HashIndexArgs) args := new(HashIndexArgs)
@ -246,10 +209,9 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
} }
block := api.xeth().EthBlockByHash(args.Hash) block := api.xeth().EthBlockByHash(args.Hash)
br := NewBlockRes(block) br := NewBlockRes(block, true)
br.fullTx = true
if args.Index > int64(len(br.Transactions)) || args.Index < 0 { if args.Index >= int64(len(br.Transactions)) || args.Index < 0 {
return NewValidationError("Index", "does not exist") return NewValidationError("Index", "does not exist")
} }
*reply = br.Transactions[args.Index] *reply = br.Transactions[args.Index]
@ -260,10 +222,9 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
} }
block := api.xeth().EthBlockByNumber(args.BlockNumber) block := api.xeth().EthBlockByNumber(args.BlockNumber)
v := NewBlockRes(block) v := NewBlockRes(block, true)
v.fullTx = true
if args.Index > int64(len(v.Transactions)) || args.Index < 0 { if args.Index >= int64(len(v.Transactions)) || args.Index < 0 {
return NewValidationError("Index", "does not exist") return NewValidationError("Index", "does not exist")
} }
*reply = v.Transactions[args.Index] *reply = v.Transactions[args.Index]
@ -273,14 +234,14 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
return err return err
} }
br := NewBlockRes(api.xeth().EthBlockByHash(args.Hash)) br := NewBlockRes(api.xeth().EthBlockByHash(args.Hash), false)
if args.Index > int64(len(br.Uncles)) || args.Index < 0 { if args.Index >= int64(len(br.Uncles)) || args.Index < 0 {
return NewValidationError("Index", "does not exist") return NewValidationError("Index", "does not exist")
} }
uhash := br.Uncles[args.Index].Hex() uhash := br.Uncles[args.Index]
uncle := NewBlockRes(api.xeth().EthBlockByHash(uhash)) uncle := NewBlockRes(api.xeth().EthBlockByHash(uhash.String()), false)
*reply = uncle *reply = uncle
case "eth_getUncleByBlockNumberAndIndex": case "eth_getUncleByBlockNumberAndIndex":
@ -290,15 +251,14 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
} }
block := api.xeth().EthBlockByNumber(args.BlockNumber) block := api.xeth().EthBlockByNumber(args.BlockNumber)
v := NewBlockRes(block) v := NewBlockRes(block, true)
v.fullTx = true
if args.Index > int64(len(v.Uncles)) || args.Index < 0 { if args.Index >= int64(len(v.Uncles)) || args.Index < 0 {
return NewValidationError("Index", "does not exist") return NewValidationError("Index", "does not exist")
} }
uhash := v.Uncles[args.Index].Hex() uhash := v.Uncles[args.Index]
uncle := NewBlockRes(api.xeth().EthBlockByHash(uhash)) uncle := NewBlockRes(api.xeth().EthBlockByHash(uhash.String()), false)
*reply = uncle *reply = uncle
case "eth_getCompilers": case "eth_getCompilers":
@ -312,18 +272,13 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
return err return err
} }
opts := toFilterOptions(args) id := api.xeth().RegisterFilter(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics)
id := api.xeth().RegisterFilter(opts)
*reply = common.ToHex(big.NewInt(int64(id)).Bytes()) *reply = common.ToHex(big.NewInt(int64(id)).Bytes())
case "eth_newBlockFilter": case "eth_newBlockFilter":
args := new(FilterStringArgs) args := new(FilterStringArgs)
if err := json.Unmarshal(req.Params, &args); err != nil { if err := json.Unmarshal(req.Params, &args); err != nil {
return err return err
} }
if err := args.requirements(); err != nil {
return err
}
id := api.xeth().NewFilterString(args.Word) id := api.xeth().NewFilterString(args.Word)
*reply = common.ToHex(big.NewInt(int64(id)).Bytes()) *reply = common.ToHex(big.NewInt(int64(id)).Bytes())
case "eth_uninstallFilter": case "eth_uninstallFilter":
@ -349,8 +304,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
if err := json.Unmarshal(req.Params, &args); err != nil { if err := json.Unmarshal(req.Params, &args); err != nil {
return err return err
} }
opts := toFilterOptions(args) *reply = NewLogsRes(api.xeth().AllLogs(args.Earliest, args.Latest, args.Skip, args.Max, args.Address, args.Topics))
*reply = NewLogsRes(api.xeth().AllLogs(opts))
case "eth_getWork": case "eth_getWork":
api.xeth().SetMining(true) api.xeth().SetMining(true)
*reply = api.xeth().RemoteMining().GetWork() *reply = api.xeth().RemoteMining().GetWork()
@ -359,7 +313,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
if err := json.Unmarshal(req.Params, &args); err != nil { if err := json.Unmarshal(req.Params, &args); err != nil {
return err return err
} }
*reply = api.xeth().RemoteMining().SubmitWork(args.Nonce, args.Digest, args.Header) *reply = api.xeth().RemoteMining().SubmitWork(args.Nonce, common.HexToHash(args.Digest), common.HexToHash(args.Header))
case "db_putString": case "db_putString":
args := new(DbArgs) args := new(DbArgs)
if err := json.Unmarshal(req.Params, &args); err != nil { if err := json.Unmarshal(req.Params, &args); err != nil {
@ -370,7 +324,8 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
return err return err
} }
api.db.Put([]byte(args.Database+args.Key), args.Value) api.xeth().DbPut([]byte(args.Database+args.Key), args.Value)
*reply = true *reply = true
case "db_getString": case "db_getString":
args := new(DbArgs) args := new(DbArgs)
@ -382,7 +337,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
return err return err
} }
res, _ := api.db.Get([]byte(args.Database + args.Key)) res, _ := api.xeth().DbGet([]byte(args.Database + args.Key))
*reply = string(res) *reply = string(res)
case "db_putHex": case "db_putHex":
args := new(DbHexArgs) args := new(DbHexArgs)
@ -394,7 +349,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
return err return err
} }
api.db.Put([]byte(args.Database+args.Key), args.Value) api.xeth().DbPut([]byte(args.Database+args.Key), args.Value)
*reply = true *reply = true
case "db_getHex": case "db_getHex":
args := new(DbHexArgs) args := new(DbHexArgs)
@ -406,7 +361,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
return err return err
} }
res, _ := api.db.Get([]byte(args.Database + args.Key)) res, _ := api.xeth().DbGet([]byte(args.Database + args.Key))
*reply = common.ToHex(res) *reply = common.ToHex(res)
case "shh_version": case "shh_version":
*reply = api.xeth().WhisperVersion() *reply = api.xeth().WhisperVersion()
@ -444,7 +399,7 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
return err return err
} }
opts := new(xeth.Options) opts := new(xeth.Options)
opts.From = args.From // opts.From = args.From
opts.To = args.To opts.To = args.To
opts.Topics = args.Topics opts.Topics = args.Topics
id := api.xeth().NewWhisperFilter(opts) id := api.xeth().NewWhisperFilter(opts)
@ -494,46 +449,3 @@ func (api *EthereumApi) GetRequestReply(req *RpcRequest, reply *interface{}) err
rpclogger.DebugDetailf("Reply: %T %s", reply, reply) rpclogger.DebugDetailf("Reply: %T %s", reply, reply)
return nil return nil
} }
func toFilterOptions(options *BlockFilterArgs) *core.FilterOptions {
var opts core.FilterOptions
// Convert optional address slice/string to byte slice
if str, ok := options.Address.(string); ok {
opts.Address = []common.Address{common.HexToAddress(str)}
} else if slice, ok := options.Address.([]interface{}); ok {
bslice := make([]common.Address, len(slice))
for i, addr := range slice {
if saddr, ok := addr.(string); ok {
bslice[i] = common.HexToAddress(saddr)
}
}
opts.Address = bslice
}
opts.Earliest = options.Earliest
opts.Latest = options.Latest
topics := make([][]common.Hash, len(options.Topics))
for i, topicDat := range options.Topics {
if slice, ok := topicDat.([]interface{}); ok {
topics[i] = make([]common.Hash, len(slice))
for j, topic := range slice {
topics[i][j] = common.HexToHash(topic.(string))
}
} else if str, ok := topicDat.(string); ok {
topics[i] = []common.Hash{common.HexToHash(str)}
}
}
opts.Topics = topics
return &opts
}
/*
Work() chan<- *types.Block
SetWorkCh(chan<- Work)
Stop()
Start()
Rate() uint64
*/

View file

@ -6,7 +6,7 @@ import (
"testing" "testing"
// "time" // "time"
"github.com/ethereum/go-ethereum/xeth" // "github.com/ethereum/go-ethereum/xeth"
) )
func TestWeb3Sha3(t *testing.T) { func TestWeb3Sha3(t *testing.T) {
@ -26,49 +26,48 @@ func TestWeb3Sha3(t *testing.T) {
} }
} }
func TestDbStr(t *testing.T) { // func TestDbStr(t *testing.T) {
jsonput := `{"jsonrpc":"2.0","method":"db_putString","params":["testDB","myKey","myString"],"id":64}` // jsonput := `{"jsonrpc":"2.0","method":"db_putString","params":["testDB","myKey","myString"],"id":64}`
jsonget := `{"jsonrpc":"2.0","method":"db_getString","params":["testDB","myKey"],"id":64}` // jsonget := `{"jsonrpc":"2.0","method":"db_getString","params":["testDB","myKey"],"id":64}`
expected := "myString" // expected := "myString"
xeth := &xeth.XEth{} // xeth := &xeth.XEth{}
api := NewEthereumApi(xeth, "") // api := NewEthereumApi(xeth)
defer api.db.Close() // var response interface{}
var response interface{}
var req RpcRequest // var req RpcRequest
json.Unmarshal([]byte(jsonput), &req) // json.Unmarshal([]byte(jsonput), &req)
_ = api.GetRequestReply(&req, &response) // _ = api.GetRequestReply(&req, &response)
json.Unmarshal([]byte(jsonget), &req) // json.Unmarshal([]byte(jsonget), &req)
_ = api.GetRequestReply(&req, &response) // _ = api.GetRequestReply(&req, &response)
if response.(string) != expected { // if response.(string) != expected {
t.Errorf("Expected %s got %s", expected, response) // t.Errorf("Expected %s got %s", expected, response)
} // }
} // }
func TestDbHexStr(t *testing.T) { // func TestDbHexStr(t *testing.T) {
jsonput := `{"jsonrpc":"2.0","method":"db_putHex","params":["testDB","beefKey","0xbeef"],"id":64}` // jsonput := `{"jsonrpc":"2.0","method":"db_putHex","params":["testDB","beefKey","0xbeef"],"id":64}`
jsonget := `{"jsonrpc":"2.0","method":"db_getHex","params":["testDB","beefKey"],"id":64}` // jsonget := `{"jsonrpc":"2.0","method":"db_getHex","params":["testDB","beefKey"],"id":64}`
expected := "0xbeef" // expected := "0xbeef"
xeth := &xeth.XEth{} // xeth := &xeth.XEth{}
api := NewEthereumApi(xeth, "") // api := NewEthereumApi(xeth)
defer api.db.Close() // defer api.db.Close()
var response interface{} // var response interface{}
var req RpcRequest // var req RpcRequest
json.Unmarshal([]byte(jsonput), &req) // json.Unmarshal([]byte(jsonput), &req)
_ = api.GetRequestReply(&req, &response) // _ = api.GetRequestReply(&req, &response)
json.Unmarshal([]byte(jsonget), &req) // json.Unmarshal([]byte(jsonget), &req)
_ = api.GetRequestReply(&req, &response) // _ = api.GetRequestReply(&req, &response)
if response.(string) != expected { // if response.(string) != expected {
t.Errorf("Expected %s got %s", expected, response) // t.Errorf("Expected %s got %s", expected, response)
} // }
} // }
// func TestFilterClose(t *testing.T) { // func TestFilterClose(t *testing.T) {
// t.Skip() // t.Skip()

View file

@ -1,14 +1,27 @@
package rpc package rpc
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
func blockHeight(raw interface{}, number *int64) (err error) { const (
defaultLogLimit = 100
defaultLogOffset = 0
)
func blockHeightFromJson(msg json.RawMessage, number *int64) error {
var raw interface{}
if err := json.Unmarshal(msg, &raw); err != nil {
return NewDecodeParamError(err.Error())
}
return blockHeight(raw, number)
}
func blockHeight(raw interface{}, number *int64) error {
// Parse as integer // Parse as integer
num, ok := raw.(float64) num, ok := raw.(float64)
if ok { if ok {
@ -19,7 +32,7 @@ func blockHeight(raw interface{}, number *int64) (err error) {
// Parse as string/hexstring // Parse as string/hexstring
str, ok := raw.(string) str, ok := raw.(string)
if !ok { if !ok {
return NewDecodeParamError("BlockNumber is not a string") return NewInvalidTypeError("", "not a number or string")
} }
switch str { switch str {
@ -34,6 +47,55 @@ func blockHeight(raw interface{}, number *int64) (err error) {
return nil return nil
} }
func numString(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 NewInvalidTypeError("", "not a number or string")
}
*number = common.String2Big(str).Int64()
return nil
}
// func toNumber(v interface{}) (int64, error) {
// var str string
// if v != nil {
// var ok bool
// str, ok = v.(string)
// if !ok {
// return 0, errors.New("is not a string or undefined")
// }
// } else {
// str = "latest"
// }
// switch str {
// case "latest":
// return -1, nil
// default:
// return int64(common.Big(v.(string)).Int64()), nil
// }
// }
// func hashString(raw interface{}, hash *string) error {
// argstr, ok := raw.(string)
// if !ok {
// return NewInvalidTypeError("", "not a string")
// }
// v := common.IsHex(argstr)
// hash = &argstr
// return nil
// }
type GetBlockByHashArgs struct { type GetBlockByHashArgs struct {
BlockHash string BlockHash string
IncludeTxs bool IncludeTxs bool
@ -41,8 +103,8 @@ type GetBlockByHashArgs struct {
func (args *GetBlockByHashArgs) UnmarshalJSON(b []byte) (err error) { func (args *GetBlockByHashArgs) UnmarshalJSON(b []byte) (err error) {
var obj []interface{} var obj []interface{}
r := bytes.NewReader(b)
if err := json.NewDecoder(r).Decode(&obj); err != nil { if err := json.Unmarshal(b, &obj); err != nil {
return NewDecodeParamError(err.Error()) return NewDecodeParamError(err.Error())
} }
@ -52,7 +114,7 @@ func (args *GetBlockByHashArgs) UnmarshalJSON(b []byte) (err error) {
argstr, ok := obj[0].(string) argstr, ok := obj[0].(string)
if !ok { if !ok {
return NewDecodeParamError("BlockHash not a string") return NewInvalidTypeError("blockHash", "not a string")
} }
args.BlockHash = argstr args.BlockHash = argstr
@ -70,8 +132,7 @@ type GetBlockByNumberArgs struct {
func (args *GetBlockByNumberArgs) UnmarshalJSON(b []byte) (err error) { func (args *GetBlockByNumberArgs) UnmarshalJSON(b []byte) (err error) {
var obj []interface{} var obj []interface{}
r := bytes.NewReader(b) if err := json.Unmarshal(b, &obj); err != nil {
if err := json.NewDecoder(r).Decode(&obj); err != nil {
return NewDecodeParamError(err.Error()) return NewDecodeParamError(err.Error())
} }
@ -81,8 +142,10 @@ func (args *GetBlockByNumberArgs) UnmarshalJSON(b []byte) (err error) {
if v, ok := obj[0].(float64); ok { if v, ok := obj[0].(float64); ok {
args.BlockNumber = int64(v) args.BlockNumber = int64(v)
} else if v, ok := obj[0].(string); ok {
args.BlockNumber = common.Big(v).Int64()
} else { } else {
args.BlockNumber = common.Big(obj[0].(string)).Int64() return NewInvalidTypeError("blockNumber", "not a number or string")
} }
if len(obj) > 1 { if len(obj) > 1 {
@ -105,7 +168,14 @@ type NewTxArgs struct {
func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) { func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) {
var obj []json.RawMessage var obj []json.RawMessage
var ext struct{ From, To, Value, Gas, GasPrice, Data string } var ext struct {
From string
To string
Value interface{}
Gas interface{}
GasPrice interface{}
Data string
}
// Decode byte slice to array of RawMessages // Decode byte slice to array of RawMessages
if err := json.Unmarshal(b, &obj); err != nil { if err := json.Unmarshal(b, &obj); err != nil {
@ -122,22 +192,45 @@ func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) {
return NewDecodeParamError(err.Error()) return NewDecodeParamError(err.Error())
} }
// var ok bool if len(ext.From) == 0 {
return NewValidationError("from", "is required")
}
args.From = ext.From args.From = ext.From
args.To = ext.To args.To = ext.To
args.Value = common.String2Big(ext.Value)
args.Gas = common.String2Big(ext.Gas)
args.GasPrice = common.String2Big(ext.GasPrice)
args.Data = ext.Data args.Data = ext.Data
var num int64
if ext.Value == nil {
return NewValidationError("value", "is required")
} else {
if err := numString(ext.Value, &num); err != nil {
return err
}
}
args.Value = big.NewInt(num)
if ext.Gas == nil {
return NewValidationError("gas", "is required")
} else {
if err := numString(ext.Gas, &num); err != nil {
return err
}
}
args.Gas = big.NewInt(num)
if ext.GasPrice == nil {
return NewValidationError("gasprice", "is required")
} else {
if err := numString(ext.GasPrice, &num); err != nil {
return err
}
}
args.GasPrice = big.NewInt(num)
// Check for optional BlockNumber param // Check for optional BlockNumber param
if len(obj) > 1 { if len(obj) > 1 {
var raw interface{} if err := blockHeightFromJson(obj[1], &args.BlockNumber); err != nil {
if err = json.Unmarshal(obj[1], &raw); err != nil {
return NewDecodeParamError(err.Error())
}
if err := blockHeight(raw, &args.BlockNumber); err != nil {
return err return err
} }
} }
@ -145,10 +238,90 @@ func (args *NewTxArgs) UnmarshalJSON(b []byte) (err error) {
return nil return nil
} }
func (args *NewTxArgs) requirements() error { type CallArgs struct {
if len(args.From) == 0 { From string
return NewValidationError("From", "Is required") To string
Value *big.Int
Gas *big.Int
GasPrice *big.Int
Data string
BlockNumber int64
}
func (args *CallArgs) UnmarshalJSON(b []byte) (err error) {
var obj []json.RawMessage
var ext struct {
From string
To string
Value interface{}
Gas interface{}
GasPrice interface{}
Data string
} }
// Decode byte slice to array of RawMessages
if err := json.Unmarshal(b, &obj); err != nil {
return NewDecodeParamError(err.Error())
}
// Check for sufficient params
if len(obj) < 1 {
return NewInsufficientParamsError(len(obj), 1)
}
// Decode 0th RawMessage to temporary struct
if err := json.Unmarshal(obj[0], &ext); err != nil {
return NewDecodeParamError(err.Error())
}
if len(ext.From) == 0 {
return NewValidationError("from", "is required")
}
args.From = ext.From
if len(ext.To) == 0 {
return NewValidationError("to", "is required")
}
args.To = ext.To
var num int64
if ext.Value == nil {
num = int64(0)
} else {
if err := numString(ext.Value, &num); err != nil {
return err
}
}
args.Value = big.NewInt(num)
if ext.Gas == nil {
num = int64(0)
} else {
if err := numString(ext.Gas, &num); err != nil {
return err
}
}
args.Gas = big.NewInt(num)
if ext.GasPrice == nil {
num = int64(0)
} else {
if err := numString(ext.GasPrice, &num); err != nil {
return err
}
}
args.GasPrice = big.NewInt(num)
args.Data = ext.Data
// Check for optional BlockNumber param
if len(obj) > 1 {
if err := blockHeightFromJson(obj[1], &args.BlockNumber); err != nil {
return err
}
}
return nil return nil
} }
@ -169,7 +342,7 @@ func (args *GetStorageArgs) UnmarshalJSON(b []byte) (err error) {
addstr, ok := obj[0].(string) addstr, ok := obj[0].(string)
if !ok { if !ok {
return NewDecodeParamError("Address is not a string") return NewInvalidTypeError("address", "not a string")
} }
args.Address = addstr args.Address = addstr
@ -182,13 +355,6 @@ func (args *GetStorageArgs) UnmarshalJSON(b []byte) (err error) {
return nil return nil
} }
func (args *GetStorageArgs) requirements() error {
if len(args.Address) == 0 {
return NewValidationError("Address", "cannot be blank")
}
return nil
}
type GetStorageAtArgs struct { type GetStorageAtArgs struct {
Address string Address string
Key string Key string
@ -207,13 +373,13 @@ func (args *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) {
addstr, ok := obj[0].(string) addstr, ok := obj[0].(string)
if !ok { if !ok {
return NewDecodeParamError("Address is not a string") return NewInvalidTypeError("address", "not a string")
} }
args.Address = addstr args.Address = addstr
keystr, ok := obj[1].(string) keystr, ok := obj[1].(string)
if !ok { if !ok {
return NewDecodeParamError("Key is not a string") return NewInvalidTypeError("key", "not a string")
} }
args.Key = keystr args.Key = keystr
@ -226,17 +392,6 @@ func (args *GetStorageAtArgs) UnmarshalJSON(b []byte) (err error) {
return nil return nil
} }
func (args *GetStorageAtArgs) requirements() error {
if len(args.Address) == 0 {
return NewValidationError("Address", "cannot be blank")
}
if len(args.Key) == 0 {
return NewValidationError("Key", "cannot be blank")
}
return nil
}
type GetTxCountArgs struct { type GetTxCountArgs struct {
Address string Address string
BlockNumber int64 BlockNumber int64
@ -254,7 +409,7 @@ func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) {
addstr, ok := obj[0].(string) addstr, ok := obj[0].(string)
if !ok { if !ok {
return NewDecodeParamError("Address is not a string") return NewInvalidTypeError("address", "not a string")
} }
args.Address = addstr args.Address = addstr
@ -267,13 +422,6 @@ func (args *GetTxCountArgs) UnmarshalJSON(b []byte) (err error) {
return nil return nil
} }
func (args *GetTxCountArgs) requirements() error {
if len(args.Address) == 0 {
return NewValidationError("Address", "cannot be blank")
}
return nil
}
type GetBalanceArgs struct { type GetBalanceArgs struct {
Address string Address string
BlockNumber int64 BlockNumber int64
@ -291,7 +439,7 @@ func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) {
addstr, ok := obj[0].(string) addstr, ok := obj[0].(string)
if !ok { if !ok {
return NewDecodeParamError("Address is not a string") return NewInvalidTypeError("address", "not a string")
} }
args.Address = addstr args.Address = addstr
@ -304,13 +452,6 @@ func (args *GetBalanceArgs) UnmarshalJSON(b []byte) (err error) {
return nil return nil
} }
func (args *GetBalanceArgs) requirements() error {
if len(args.Address) == 0 {
return NewValidationError("Address", "cannot be blank")
}
return nil
}
type GetDataArgs struct { type GetDataArgs struct {
Address string Address string
BlockNumber int64 BlockNumber int64
@ -328,7 +469,7 @@ func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) {
addstr, ok := obj[0].(string) addstr, ok := obj[0].(string)
if !ok { if !ok {
return NewDecodeParamError("Address is not a string") return NewInvalidTypeError("address", "not a string")
} }
args.Address = addstr args.Address = addstr
@ -341,13 +482,6 @@ func (args *GetDataArgs) UnmarshalJSON(b []byte) (err error) {
return nil return nil
} }
func (args *GetDataArgs) requirements() error {
if len(args.Address) == 0 {
return NewValidationError("Address", "cannot be blank")
}
return nil
}
type BlockNumIndexArgs struct { type BlockNumIndexArgs struct {
BlockNumber int64 BlockNumber int64
Index int64 Index int64
@ -355,8 +489,7 @@ type BlockNumIndexArgs struct {
func (args *BlockNumIndexArgs) UnmarshalJSON(b []byte) (err error) { func (args *BlockNumIndexArgs) UnmarshalJSON(b []byte) (err error) {
var obj []interface{} var obj []interface{}
r := bytes.NewReader(b) if err := json.Unmarshal(b, &obj); err != nil {
if err := json.NewDecoder(r).Decode(&obj); err != nil {
return NewDecodeParamError(err.Error()) return NewDecodeParamError(err.Error())
} }
@ -364,16 +497,14 @@ func (args *BlockNumIndexArgs) UnmarshalJSON(b []byte) (err error) {
return NewInsufficientParamsError(len(obj), 1) return NewInsufficientParamsError(len(obj), 1)
} }
arg0, ok := obj[0].(string) if err := blockHeight(obj[0], &args.BlockNumber); err != nil {
if !ok { return err
return NewDecodeParamError("BlockNumber is not string")
} }
args.BlockNumber = common.Big(arg0).Int64()
if len(obj) > 1 { if len(obj) > 1 {
arg1, ok := obj[1].(string) arg1, ok := obj[1].(string)
if !ok { if !ok {
return NewDecodeParamError("Index not a string") return NewInvalidTypeError("index", "not a string")
} }
args.Index = common.Big(arg1).Int64() args.Index = common.Big(arg1).Int64()
} }
@ -388,8 +519,7 @@ type HashIndexArgs struct {
func (args *HashIndexArgs) UnmarshalJSON(b []byte) (err error) { func (args *HashIndexArgs) UnmarshalJSON(b []byte) (err error) {
var obj []interface{} var obj []interface{}
r := bytes.NewReader(b) if err := json.Unmarshal(b, &obj); err != nil {
if err := json.NewDecoder(r).Decode(&obj); err != nil {
return NewDecodeParamError(err.Error()) return NewDecodeParamError(err.Error())
} }
@ -399,14 +529,14 @@ func (args *HashIndexArgs) UnmarshalJSON(b []byte) (err error) {
arg0, ok := obj[0].(string) arg0, ok := obj[0].(string)
if !ok { if !ok {
return NewDecodeParamError("Hash not a string") return NewInvalidTypeError("hash", "not a string")
} }
args.Hash = arg0 args.Hash = arg0
if len(obj) > 1 { if len(obj) > 1 {
arg1, ok := obj[1].(string) arg1, ok := obj[1].(string)
if !ok { if !ok {
return NewDecodeParamError("Index not a string") return NewInvalidTypeError("index", "not a string")
} }
args.Index = common.Big(arg1).Int64() args.Index = common.Big(arg1).Int64()
} }
@ -420,24 +550,27 @@ type Sha3Args struct {
func (args *Sha3Args) UnmarshalJSON(b []byte) (err error) { func (args *Sha3Args) UnmarshalJSON(b []byte) (err error) {
var obj []interface{} var obj []interface{}
r := bytes.NewReader(b) if err := json.Unmarshal(b, &obj); err != nil {
if err := json.NewDecoder(r).Decode(&obj); err != nil {
return NewDecodeParamError(err.Error()) return NewDecodeParamError(err.Error())
} }
if len(obj) < 1 { if len(obj) < 1 {
return NewInsufficientParamsError(len(obj), 1) return NewInsufficientParamsError(len(obj), 1)
} }
args.Data = obj[0].(string)
argstr, ok := obj[0].(string)
if !ok {
return NewInvalidTypeError("data", "is not a string")
}
args.Data = argstr
return nil return nil
} }
type BlockFilterArgs struct { type BlockFilterArgs struct {
Earliest int64 Earliest int64
Latest int64 Latest int64
Address interface{} Address []string
Topics []interface{} Topics [][]string
Skip int Skip int
Max int Max int
} }
@ -446,10 +579,10 @@ func (args *BlockFilterArgs) UnmarshalJSON(b []byte) (err error) {
var obj []struct { var obj []struct {
FromBlock interface{} `json:"fromBlock"` FromBlock interface{} `json:"fromBlock"`
ToBlock interface{} `json:"toBlock"` ToBlock interface{} `json:"toBlock"`
Limit string `json:"limit"` Limit interface{} `json:"limit"`
Offset string `json:"offset"` Offset interface{} `json:"offset"`
Address string `json:"address"` Address interface{} `json:"address"`
Topics []interface{} `json:"topics"` Topics interface{} `json:"topics"`
} }
if err = json.Unmarshal(b, &obj); err != nil { if err = json.Unmarshal(b, &obj); err != nil {
@ -460,36 +593,113 @@ func (args *BlockFilterArgs) UnmarshalJSON(b []byte) (err error) {
return NewInsufficientParamsError(len(obj), 1) return NewInsufficientParamsError(len(obj), 1)
} }
fromstr, ok := obj[0].FromBlock.(string) // args.Earliest, err = toNumber(obj[0].ToBlock)
// if err != nil {
// return NewDecodeParamError(fmt.Sprintf("FromBlock %v", err))
// }
// args.Latest, err = toNumber(obj[0].FromBlock)
// if err != nil {
// return NewDecodeParamError(fmt.Sprintf("ToBlock %v", err))
var num int64
// if blank then latest
if obj[0].FromBlock == nil {
num = -1
} else {
if err := blockHeight(obj[0].FromBlock, &num); err != nil {
return err
}
}
// if -2 or other "silly" number, use latest
if num < 0 {
args.Earliest = -1 //latest block
} else {
args.Earliest = num
}
// if blank than latest
if obj[0].ToBlock == nil {
num = -1
} else {
if err := blockHeight(obj[0].ToBlock, &num); err != nil {
return err
}
}
args.Latest = num
if obj[0].Limit == nil {
num = defaultLogLimit
} else {
if err := numString(obj[0].Limit, &num); err != nil {
return err
}
}
args.Max = int(num)
if obj[0].Offset == nil {
num = defaultLogOffset
} else {
if err := numString(obj[0].Offset, &num); err != nil {
return err
}
}
args.Skip = int(num)
if obj[0].Address != nil {
marg, ok := obj[0].Address.([]interface{})
if ok {
v := make([]string, len(marg))
for i, arg := range marg {
argstr, ok := arg.(string)
if !ok { if !ok {
return NewDecodeParamError("FromBlock is not a string") return NewInvalidTypeError(fmt.Sprintf("address[%d]", i), "is not a string")
}
v[i] = argstr
}
args.Address = v
} else {
argstr, ok := obj[0].Address.(string)
if ok {
v := make([]string, 1)
v[0] = argstr
args.Address = v
} else {
return NewInvalidTypeError("address", "is not a string or array")
}
}
} }
switch fromstr { if obj[0].Topics != nil {
case "latest": other, ok := obj[0].Topics.([]interface{})
args.Earliest = -1 if ok {
default: topicdbl := make([][]string, len(other))
args.Earliest = int64(common.Big(obj[0].FromBlock.(string)).Int64()) for i, iv := range other {
if argstr, ok := iv.(string); ok {
// Found a string, push into first element of array
topicsgl := make([]string, 1)
topicsgl[0] = argstr
topicdbl[i] = topicsgl
} else if argarray, ok := iv.([]interface{}); ok {
// Found an array of other
topicdbl[i] = make([]string, len(argarray))
for j, jv := range argarray {
if v, ok := jv.(string); ok {
topicdbl[i][j] = v
} else {
return NewInvalidTypeError(fmt.Sprintf("topic[%d][%d]", i, j), "is not a string")
} }
tostr, ok := obj[0].ToBlock.(string)
if !ok {
return NewDecodeParamError("ToBlock is not a string")
} }
} else {
switch tostr { return NewInvalidTypeError(fmt.Sprintf("topic[%d]", i), "not a string or array")
case "latest": }
args.Latest = -1 }
case "pending": args.Topics = topicdbl
args.Latest = -2 return nil
default: } else {
args.Latest = int64(common.Big(obj[0].ToBlock.(string)).Int64()) return NewInvalidTypeError("topic", "is not a string or array")
}
} }
args.Max = int(common.Big(obj[0].Limit).Int64())
args.Skip = int(common.Big(obj[0].Offset).Int64())
args.Address = obj[0].Address
args.Topics = obj[0].Topics
return nil return nil
} }
@ -514,19 +724,19 @@ func (args *DbArgs) UnmarshalJSON(b []byte) (err error) {
var ok bool var ok bool
if objstr, ok = obj[0].(string); !ok { if objstr, ok = obj[0].(string); !ok {
return NewDecodeParamError("Database is not a string") return NewInvalidTypeError("database", "not a string")
} }
args.Database = objstr args.Database = objstr
if objstr, ok = obj[1].(string); !ok { if objstr, ok = obj[1].(string); !ok {
return NewDecodeParamError("Key is not a string") return NewInvalidTypeError("key", "not a string")
} }
args.Key = objstr args.Key = objstr
if len(obj) > 2 { if len(obj) > 2 {
objstr, ok = obj[2].(string) objstr, ok = obj[2].(string)
if !ok { if !ok {
return NewDecodeParamError("Value is not a string") return NewInvalidTypeError("value", "not a string")
} }
args.Value = []byte(objstr) args.Value = []byte(objstr)
@ -565,19 +775,19 @@ func (args *DbHexArgs) UnmarshalJSON(b []byte) (err error) {
var ok bool var ok bool
if objstr, ok = obj[0].(string); !ok { if objstr, ok = obj[0].(string); !ok {
return NewDecodeParamError("Database is not a string") return NewInvalidTypeError("database", "not a string")
} }
args.Database = objstr args.Database = objstr
if objstr, ok = obj[1].(string); !ok { if objstr, ok = obj[1].(string); !ok {
return NewDecodeParamError("Key is not a string") return NewInvalidTypeError("key", "not a string")
} }
args.Key = objstr args.Key = objstr
if len(obj) > 2 { if len(obj) > 2 {
objstr, ok = obj[2].(string) objstr, ok = obj[2].(string)
if !ok { if !ok {
return NewDecodeParamError("Value is not a string") return NewInvalidTypeError("value", "not a string")
} }
args.Value = common.FromHex(objstr) args.Value = common.FromHex(objstr)
@ -611,8 +821,8 @@ func (args *WhisperMessageArgs) UnmarshalJSON(b []byte) (err error) {
To string To string
From string From string
Topics []string Topics []string
Priority string Priority interface{}
Ttl string Ttl interface{}
} }
if err = json.Unmarshal(b, &obj); err != nil { if err = json.Unmarshal(b, &obj); err != nil {
@ -626,8 +836,17 @@ func (args *WhisperMessageArgs) UnmarshalJSON(b []byte) (err error) {
args.To = obj[0].To args.To = obj[0].To
args.From = obj[0].From args.From = obj[0].From
args.Topics = obj[0].Topics args.Topics = obj[0].Topics
args.Priority = uint32(common.Big(obj[0].Priority).Int64())
args.Ttl = uint32(common.Big(obj[0].Ttl).Int64()) var num int64
if err := numString(obj[0].Priority, &num); err != nil {
return err
}
args.Priority = uint32(num)
if err := numString(obj[0].Ttl, &num); err != nil {
return err
}
args.Ttl = uint32(num)
return nil return nil
} }
@ -638,14 +857,18 @@ type CompileArgs struct {
func (args *CompileArgs) UnmarshalJSON(b []byte) (err error) { func (args *CompileArgs) UnmarshalJSON(b []byte) (err error) {
var obj []interface{} var obj []interface{}
r := bytes.NewReader(b) if err := json.Unmarshal(b, &obj); err != nil {
if err := json.NewDecoder(r).Decode(&obj); err != nil {
return NewDecodeParamError(err.Error()) return NewDecodeParamError(err.Error())
} }
if len(obj) > 0 { if len(obj) < 1 {
args.Source = obj[0].(string) return NewInsufficientParamsError(len(obj), 1)
} }
argstr, ok := obj[0].(string)
if !ok {
return NewInvalidTypeError("arg0", "is not a string")
}
args.Source = argstr
return nil return nil
} }
@ -656,8 +879,7 @@ type FilterStringArgs struct {
func (args *FilterStringArgs) UnmarshalJSON(b []byte) (err error) { func (args *FilterStringArgs) UnmarshalJSON(b []byte) (err error) {
var obj []interface{} var obj []interface{}
r := bytes.NewReader(b) if err := json.Unmarshal(b, &obj); err != nil {
if err := json.NewDecoder(r).Decode(&obj); err != nil {
return NewDecodeParamError(err.Error()) return NewDecodeParamError(err.Error())
} }
@ -668,20 +890,15 @@ func (args *FilterStringArgs) UnmarshalJSON(b []byte) (err error) {
var argstr string var argstr string
argstr, ok := obj[0].(string) argstr, ok := obj[0].(string)
if !ok { if !ok {
return NewDecodeParamError("Filter is not a string") return NewInvalidTypeError("filter", "not a string")
} }
args.Word = argstr switch argstr {
return nil
}
func (args *FilterStringArgs) requirements() error {
switch args.Word {
case "latest", "pending": case "latest", "pending":
break break
default: default:
return NewValidationError("Word", "Must be `latest` or `pending`") return NewValidationError("Word", "Must be `latest` or `pending`")
} }
args.Word = argstr
return nil return nil
} }
@ -690,9 +907,8 @@ type FilterIdArgs struct {
} }
func (args *FilterIdArgs) UnmarshalJSON(b []byte) (err error) { func (args *FilterIdArgs) UnmarshalJSON(b []byte) (err error) {
var obj []string var obj []interface{}
r := bytes.NewReader(b) if err := json.Unmarshal(b, &obj); err != nil {
if err := json.NewDecoder(r).Decode(&obj); err != nil {
return NewDecodeParamError(err.Error()) return NewDecodeParamError(err.Error())
} }
@ -700,7 +916,11 @@ func (args *FilterIdArgs) UnmarshalJSON(b []byte) (err error) {
return NewInsufficientParamsError(len(obj), 1) return NewInsufficientParamsError(len(obj), 1)
} }
args.Id = int(common.Big(obj[0]).Int64()) var num int64
if err := numString(obj[0], &num); err != nil {
return err
}
args.Id = int(num)
return nil return nil
} }
@ -710,9 +930,8 @@ type WhisperIdentityArgs struct {
} }
func (args *WhisperIdentityArgs) UnmarshalJSON(b []byte) (err error) { func (args *WhisperIdentityArgs) UnmarshalJSON(b []byte) (err error) {
var obj []string var obj []interface{}
r := bytes.NewReader(b) if err := json.Unmarshal(b, &obj); err != nil {
if err := json.NewDecoder(r).Decode(&obj); err != nil {
return NewDecodeParamError(err.Error()) return NewDecodeParamError(err.Error())
} }
@ -720,7 +939,14 @@ func (args *WhisperIdentityArgs) UnmarshalJSON(b []byte) (err error) {
return NewInsufficientParamsError(len(obj), 1) return NewInsufficientParamsError(len(obj), 1)
} }
args.Identity = obj[0] argstr, ok := obj[0].(string)
if !ok {
return NewInvalidTypeError("arg0", "not a string")
}
// if !common.IsHex(argstr) {
// return NewValidationError("arg0", "not a hexstring")
// }
args.Identity = argstr
return nil return nil
} }
@ -733,9 +959,8 @@ type WhisperFilterArgs struct {
func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) { func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) {
var obj []struct { var obj []struct {
To string To interface{}
From string Topics []interface{}
Topics []string
} }
if err = json.Unmarshal(b, &obj); err != nil { if err = json.Unmarshal(b, &obj); err != nil {
@ -746,17 +971,30 @@ func (args *WhisperFilterArgs) UnmarshalJSON(b []byte) (err error) {
return NewInsufficientParamsError(len(obj), 1) return NewInsufficientParamsError(len(obj), 1)
} }
args.To = obj[0].To var argstr string
args.From = obj[0].From argstr, ok := obj[0].To.(string)
args.Topics = obj[0].Topics if !ok {
return NewInvalidTypeError("to", "is not a string")
}
args.To = argstr
t := make([]string, len(obj[0].Topics))
for i, j := range obj[0].Topics {
argstr, ok := j.(string)
if !ok {
return NewInvalidTypeError("topics["+string(i)+"]", "is not a string")
}
t[i] = argstr
}
args.Topics = t
return nil return nil
} }
type SubmitWorkArgs struct { type SubmitWorkArgs struct {
Nonce uint64 Nonce uint64
Header common.Hash Header string
Digest common.Hash Digest string
} }
func (args *SubmitWorkArgs) UnmarshalJSON(b []byte) (err error) { func (args *SubmitWorkArgs) UnmarshalJSON(b []byte) (err error) {
@ -772,21 +1010,21 @@ func (args *SubmitWorkArgs) UnmarshalJSON(b []byte) (err error) {
var objstr string var objstr string
var ok bool var ok bool
if objstr, ok = obj[0].(string); !ok { if objstr, ok = obj[0].(string); !ok {
return NewDecodeParamError("Nonce is not a string") return NewInvalidTypeError("nonce", "not a string")
} }
args.Nonce = common.String2Big(objstr).Uint64() args.Nonce = common.String2Big(objstr).Uint64()
if objstr, ok = obj[1].(string); !ok { if objstr, ok = obj[1].(string); !ok {
return NewDecodeParamError("Header is not a string") return NewInvalidTypeError("header", "not a string")
} }
args.Header = common.HexToHash(objstr) args.Header = objstr
if objstr, ok = obj[2].(string); !ok { if objstr, ok = obj[2].(string); !ok {
return NewDecodeParamError("Digest is not a string") return NewInvalidTypeError("digest", "not a string")
} }
args.Digest = common.HexToHash(objstr) args.Digest = objstr
return nil return nil
} }

File diff suppressed because it is too large Load diff

View file

@ -2,12 +2,15 @@ package rpc
import ( import (
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net"
"net/http" "net/http"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/xeth" "github.com/ethereum/go-ethereum/xeth"
"github.com/rs/cors"
) )
var rpclogger = logger.NewLogger("RPC") var rpclogger = logger.NewLogger("RPC")
@ -17,13 +20,36 @@ const (
maxSizeReqLength = 1024 * 1024 // 1MB maxSizeReqLength = 1024 * 1024 // 1MB
) )
func Start(pipe *xeth.XEth, config RpcConfig) error {
l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", config.ListenAddress, config.ListenPort))
if err != nil {
rpclogger.Errorf("Can't listen on %s:%d: %v", config.ListenAddress, config.ListenPort, err)
return err
}
var handler http.Handler
if len(config.CorsDomain) > 0 {
var opts cors.Options
opts.AllowedMethods = []string{"POST"}
opts.AllowedOrigins = []string{config.CorsDomain}
c := cors.New(opts)
handler = c.Handler(JSONRPC(pipe))
} else {
handler = JSONRPC(pipe)
}
go http.Serve(l, handler)
return nil
}
// JSONRPC returns a handler that implements the Ethereum JSON-RPC API. // JSONRPC returns a handler that implements the Ethereum JSON-RPC API.
func JSONRPC(pipe *xeth.XEth, dataDir string) http.Handler { func JSONRPC(pipe *xeth.XEth) http.Handler {
api := NewEthereumApi(pipe, dataDir) api := NewEthereumApi(pipe)
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
// TODO this needs to be configurable w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
// Limit request size to resist DoS // Limit request size to resist DoS
if req.ContentLength > maxSizeReqLength { if req.ContentLength > maxSizeReqLength {
@ -76,7 +102,7 @@ func RpcResponse(api *EthereumApi, request *RpcRequest) *interface{} {
case *NotImplementedError: case *NotImplementedError:
jsonerr := &RpcErrorObject{-32601, reserr.Error()} jsonerr := &RpcErrorObject{-32601, reserr.Error()}
response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr} response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr}
case *DecodeParamError, *InsufficientParamsError, *ValidationError: case *DecodeParamError, *InsufficientParamsError, *ValidationError, *InvalidTypeError:
jsonerr := &RpcErrorObject{-32602, reserr.Error()} jsonerr := &RpcErrorObject{-32602, reserr.Error()}
response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr} response = &RpcErrorResponse{Jsonrpc: jsonrpcver, Id: request.Id, Error: jsonerr}
default: default:

View file

@ -19,8 +19,117 @@ package rpc
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"math/big"
"strings"
"github.com/ethereum/go-ethereum/common"
) )
type hexdata struct {
data []byte
}
func (d *hexdata) String() string {
return "0x" + common.Bytes2Hex(d.data)
}
func (d *hexdata) MarshalJSON() ([]byte, error) {
return json.Marshal(d.String())
}
func (d *hexdata) UnmarshalJSON(b []byte) (err error) {
d.data = common.FromHex(string(b))
return nil
}
func newHexData(input interface{}) *hexdata {
d := new(hexdata)
switch input := input.(type) {
case []byte:
d.data = input
case common.Hash:
d.data = input.Bytes()
case *common.Hash:
d.data = input.Bytes()
case common.Address:
d.data = input.Bytes()
case *common.Address:
d.data = input.Bytes()
case *big.Int:
d.data = input.Bytes()
case int64:
d.data = big.NewInt(input).Bytes()
case uint64:
d.data = big.NewInt(int64(input)).Bytes()
case int:
d.data = big.NewInt(int64(input)).Bytes()
case uint:
d.data = big.NewInt(int64(input)).Bytes()
case string: // hexstring
d.data = common.Big(input).Bytes()
default:
d.data = nil
}
return d
}
type hexnum struct {
data []byte
}
func (d *hexnum) String() string {
// Get hex string from bytes
out := common.Bytes2Hex(d.data)
// Trim leading 0s
out = strings.Trim(out, "0")
// Output "0x0" when value is 0
if len(out) == 0 {
out = "0"
}
return "0x" + out
}
func (d *hexnum) MarshalJSON() ([]byte, error) {
return json.Marshal(d.String())
}
func (d *hexnum) UnmarshalJSON(b []byte) (err error) {
d.data = common.FromHex(string(b))
return nil
}
func newHexNum(input interface{}) *hexnum {
d := new(hexnum)
d.data = newHexData(input).data
return d
}
type RpcConfig struct {
ListenAddress string
ListenPort uint
CorsDomain string
}
type InvalidTypeError struct {
method string
msg string
}
func (e *InvalidTypeError) Error() string {
return fmt.Sprintf("invalid type on field %s: %s", e.method, e.msg)
}
func NewInvalidTypeError(method, msg string) *InvalidTypeError {
return &InvalidTypeError{
method: method,
msg: msg,
}
}
type InsufficientParamsError struct { type InsufficientParamsError struct {
have int have int
want int want int

View file

@ -4,6 +4,15 @@ import (
"testing" "testing"
) )
func TestInvalidTypeError(t *testing.T) {
err := NewInvalidTypeError("testField", "not string")
expected := "invalid type on field testField: not string"
if err.Error() != expected {
t.Error(err.Error())
}
}
func TestInsufficientParamsError(t *testing.T) { func TestInsufficientParamsError(t *testing.T) {
err := NewInsufficientParamsError(0, 1) err := NewInsufficientParamsError(0, 1)
expected := "insufficient params, want 1 have 0" expected := "insufficient params, want 1 have 0"

View file

@ -1,242 +1,161 @@
package rpc package rpc
import ( import (
"encoding/json"
// "fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
) )
type BlockRes struct { type BlockRes struct {
fullTx bool fullTx bool
BlockNumber int64 `json:"number"` BlockNumber *hexnum `json:"number"`
BlockHash common.Hash `json:"hash"` BlockHash *hexdata `json:"hash"`
ParentHash common.Hash `json:"parentHash"` ParentHash *hexdata `json:"parentHash"`
Nonce [8]byte `json:"nonce"` Nonce *hexnum `json:"nonce"`
Sha3Uncles common.Hash `json:"sha3Uncles"` Sha3Uncles *hexdata `json:"sha3Uncles"`
LogsBloom types.Bloom `json:"logsBloom"` LogsBloom *hexdata `json:"logsBloom"`
TransactionRoot common.Hash `json:"transactionRoot"` TransactionRoot *hexdata `json:"transactionRoot"`
StateRoot common.Hash `json:"stateRoot"` StateRoot *hexdata `json:"stateRoot"`
Miner common.Address `json:"miner"` Miner *hexdata `json:"miner"`
Difficulty int64 `json:"difficulty"` Difficulty *hexnum `json:"difficulty"`
TotalDifficulty int64 `json:"totalDifficulty"` TotalDifficulty *hexnum `json:"totalDifficulty"`
Size int64 `json:"size"` Size *hexnum `json:"size"`
ExtraData []byte `json:"extraData"` ExtraData *hexdata `json:"extraData"`
GasLimit int64 `json:"gasLimit"` GasLimit *hexnum `json:"gasLimit"`
MinGasPrice int64 `json:"minGasPrice"` MinGasPrice *hexnum `json:"minGasPrice"`
GasUsed int64 `json:"gasUsed"` GasUsed *hexnum `json:"gasUsed"`
UnixTimestamp int64 `json:"timestamp"` UnixTimestamp *hexnum `json:"timestamp"`
Transactions []*TransactionRes `json:"transactions"` Transactions []*TransactionRes `json:"transactions"`
Uncles []common.Hash `json:"uncles"` Uncles []*hexdata `json:"uncles"`
} }
func (b *BlockRes) MarshalJSON() ([]byte, error) { func NewBlockRes(block *types.Block, fullTx bool) *BlockRes {
var ext struct { // TODO respect fullTx flag
BlockNumber string `json:"number"`
BlockHash string `json:"hash"`
ParentHash string `json:"parentHash"`
Nonce string `json:"nonce"`
Sha3Uncles string `json:"sha3Uncles"`
LogsBloom string `json:"logsBloom"`
TransactionRoot string `json:"transactionRoot"`
StateRoot string `json:"stateRoot"`
Miner string `json:"miner"`
Difficulty string `json:"difficulty"`
TotalDifficulty string `json:"totalDifficulty"`
Size string `json:"size"`
ExtraData string `json:"extraData"`
GasLimit string `json:"gasLimit"`
MinGasPrice string `json:"minGasPrice"`
GasUsed string `json:"gasUsed"`
UnixTimestamp string `json:"timestamp"`
Transactions []interface{} `json:"transactions"`
Uncles []string `json:"uncles"`
}
// convert strict types to hexified strings
ext.BlockNumber = common.ToHex(big.NewInt(b.BlockNumber).Bytes())
ext.BlockHash = b.BlockHash.Hex()
ext.ParentHash = b.ParentHash.Hex()
ext.Nonce = common.ToHex(b.Nonce[:])
ext.Sha3Uncles = b.Sha3Uncles.Hex()
ext.LogsBloom = common.ToHex(b.LogsBloom[:])
ext.TransactionRoot = b.TransactionRoot.Hex()
ext.StateRoot = b.StateRoot.Hex()
ext.Miner = b.Miner.Hex()
ext.Difficulty = common.ToHex(big.NewInt(b.Difficulty).Bytes())
ext.TotalDifficulty = common.ToHex(big.NewInt(b.TotalDifficulty).Bytes())
ext.Size = common.ToHex(big.NewInt(b.Size).Bytes())
// ext.ExtraData = common.ToHex(b.ExtraData)
ext.GasLimit = common.ToHex(big.NewInt(b.GasLimit).Bytes())
// ext.MinGasPrice = common.ToHex(big.NewInt(b.MinGasPrice).Bytes())
ext.GasUsed = common.ToHex(big.NewInt(b.GasUsed).Bytes())
ext.UnixTimestamp = common.ToHex(big.NewInt(b.UnixTimestamp).Bytes())
ext.Transactions = make([]interface{}, len(b.Transactions))
if b.fullTx {
for i, tx := range b.Transactions {
ext.Transactions[i] = tx
}
} else {
for i, tx := range b.Transactions {
ext.Transactions[i] = tx.Hash.Hex()
}
}
ext.Uncles = make([]string, len(b.Uncles))
for i, v := range b.Uncles {
ext.Uncles[i] = v.Hex()
}
return json.Marshal(ext)
}
func NewBlockRes(block *types.Block) *BlockRes {
if block == nil { if block == nil {
return &BlockRes{} return &BlockRes{}
} }
res := new(BlockRes) res := new(BlockRes)
res.BlockNumber = block.Number().Int64() res.fullTx = fullTx
res.BlockHash = block.Hash() res.BlockNumber = newHexNum(block.Number())
res.ParentHash = block.ParentHash() res.BlockHash = newHexData(block.Hash())
res.Nonce = block.Header().Nonce res.ParentHash = newHexData(block.ParentHash())
res.Sha3Uncles = block.Header().UncleHash res.Nonce = newHexNum(block.Header().Nonce)
res.LogsBloom = block.Bloom() res.Sha3Uncles = newHexData(block.Header().UncleHash)
res.TransactionRoot = block.Header().TxHash res.LogsBloom = newHexData(block.Bloom())
res.StateRoot = block.Root() res.TransactionRoot = newHexData(block.Header().TxHash)
res.Miner = block.Header().Coinbase res.StateRoot = newHexData(block.Root())
res.Difficulty = block.Difficulty().Int64() res.Miner = newHexData(block.Header().Coinbase)
if block.Td != nil { res.Difficulty = newHexNum(block.Difficulty())
res.TotalDifficulty = block.Td.Int64() res.TotalDifficulty = newHexNum(block.Td)
} res.Size = newHexNum(block.Size())
res.Size = int64(block.Size()) res.ExtraData = newHexData(block.Header().Extra)
// res.ExtraData = res.GasLimit = newHexNum(block.GasLimit())
res.GasLimit = block.GasLimit().Int64()
// res.MinGasPrice = // res.MinGasPrice =
res.GasUsed = block.GasUsed().Int64() res.GasUsed = newHexNum(block.GasUsed())
res.UnixTimestamp = block.Time() res.UnixTimestamp = newHexNum(block.Time())
res.Transactions = make([]*TransactionRes, len(block.Transactions())) res.Transactions = make([]*TransactionRes, len(block.Transactions()))
for i, tx := range block.Transactions() { for i, tx := range block.Transactions() {
v := NewTransactionRes(tx) res.Transactions[i] = NewTransactionRes(tx)
v.BlockHash = block.Hash() res.Transactions[i].BlockHash = res.BlockHash
v.BlockNumber = block.Number().Int64() res.Transactions[i].BlockNumber = res.BlockNumber
v.TxIndex = int64(i) res.Transactions[i].TxIndex = newHexNum(i)
res.Transactions[i] = v
} }
res.Uncles = make([]common.Hash, len(block.Uncles()))
res.Uncles = make([]*hexdata, len(block.Uncles()))
for i, uncle := range block.Uncles() { for i, uncle := range block.Uncles() {
res.Uncles[i] = uncle.Hash() res.Uncles[i] = newHexData(uncle.Hash())
} }
return res return res
} }
type TransactionRes struct { type TransactionRes struct {
Hash common.Hash `json:"hash"` Hash *hexdata `json:"hash"`
Nonce int64 `json:"nonce"` Nonce *hexnum `json:"nonce"`
BlockHash common.Hash `json:"blockHash,omitempty"` BlockHash *hexdata `json:"blockHash"`
BlockNumber int64 `json:"blockNumber,omitempty"` BlockNumber *hexnum `json:"blockNumber"`
TxIndex int64 `json:"transactionIndex,omitempty"` TxIndex *hexnum `json:"transactionIndex"`
From common.Address `json:"from"` From *hexdata `json:"from"`
To *common.Address `json:"to"` To *hexdata `json:"to"`
Value int64 `json:"value"` Value *hexnum `json:"value"`
Gas int64 `json:"gas"` Gas *hexnum `json:"gas"`
GasPrice int64 `json:"gasPrice"` GasPrice *hexnum `json:"gasPrice"`
Input []byte `json:"input"` Input *hexdata `json:"input"`
}
func (t *TransactionRes) MarshalJSON() ([]byte, error) {
var ext struct {
Hash string `json:"hash"`
Nonce string `json:"nonce"`
BlockHash string `json:"blockHash,omitempty"`
BlockNumber string `json:"blockNumber,omitempty"`
TxIndex string `json:"transactionIndex,omitempty"`
From string `json:"from"`
To string `json:"to"`
Value string `json:"value"`
Gas string `json:"gas"`
GasPrice string `json:"gasPrice"`
Input string `json:"input"`
}
ext.Hash = t.Hash.Hex()
ext.Nonce = common.ToHex(big.NewInt(t.Nonce).Bytes())
ext.BlockHash = t.BlockHash.Hex()
ext.BlockNumber = common.ToHex(big.NewInt(t.BlockNumber).Bytes())
ext.TxIndex = common.ToHex(big.NewInt(t.TxIndex).Bytes())
ext.From = t.From.Hex()
if t.To == nil {
ext.To = "0x00"
} else {
ext.To = t.To.Hex()
}
ext.Value = common.ToHex(big.NewInt(t.Value).Bytes())
ext.Gas = common.ToHex(big.NewInt(t.Gas).Bytes())
ext.GasPrice = common.ToHex(big.NewInt(t.GasPrice).Bytes())
ext.Input = common.ToHex(t.Input)
return json.Marshal(ext)
} }
func NewTransactionRes(tx *types.Transaction) *TransactionRes { func NewTransactionRes(tx *types.Transaction) *TransactionRes {
var v = new(TransactionRes) var v = new(TransactionRes)
v.Hash = tx.Hash() v.Hash = newHexData(tx.Hash())
v.Nonce = int64(tx.Nonce()) v.Nonce = newHexNum(tx.Nonce())
v.From, _ = tx.From() // v.BlockHash =
v.To = tx.To() // v.BlockNumber =
v.Value = tx.Value().Int64() // v.TxIndex =
v.Gas = tx.Gas().Int64() from, _ := tx.From()
v.GasPrice = tx.GasPrice().Int64() v.From = newHexData(from)
v.Input = tx.Data() 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 return v
} }
type FilterLogRes struct { // type FilterLogRes struct {
Hash string `json:"hash"` // Hash string `json:"hash"`
Address string `json:"address"` // Address string `json:"address"`
Data string `json:"data"` // Data string `json:"data"`
BlockNumber string `json:"blockNumber"` // BlockNumber string `json:"blockNumber"`
TransactionHash string `json:"transactionHash"` // TransactionHash string `json:"transactionHash"`
BlockHash string `json:"blockHash"` // BlockHash string `json:"blockHash"`
TransactionIndex string `json:"transactionIndex"` // TransactionIndex string `json:"transactionIndex"`
LogIndex string `json:"logIndex"` // LogIndex string `json:"logIndex"`
} // }
type FilterWhisperRes struct { // type FilterWhisperRes struct {
Hash string `json:"hash"` // Hash string `json:"hash"`
From string `json:"from"` // From string `json:"from"`
To string `json:"to"` // To string `json:"to"`
Expiry string `json:"expiry"` // Expiry string `json:"expiry"`
Sent string `json:"sent"` // Sent string `json:"sent"`
Ttl string `json:"ttl"` // Ttl string `json:"ttl"`
Topics string `json:"topics"` // Topics string `json:"topics"`
Payload string `json:"payload"` // Payload string `json:"payload"`
WorkProved string `json:"workProved"` // WorkProved string `json:"workProved"`
} // }
type LogRes struct { type LogRes struct {
Address string `json:"address"` Address *hexdata `json:"address"`
Topics []string `json:"topics"` Topics []*hexdata `json:"topics"`
Data string `json:"data"` Data *hexdata `json:"data"`
Number uint64 `json:"number"` BlockNumber *hexnum `json:"blockNumber"`
Hash *hexdata `json:"hash"`
LogIndex *hexnum `json:"logIndex"`
BlockHash *hexdata `json:"blockHash"`
TransactionHash *hexdata `json:"transactionHash"`
TransactionIndex *hexnum `json:"transactionIndex"`
}
func NewLogRes(log state.Log) LogRes {
var l LogRes
l.Topics = make([]*hexdata, len(log.Topics()))
for j, topic := range log.Topics() {
l.Topics[j] = newHexData(topic)
}
l.Address = newHexData(log.Address())
l.Data = newHexData(log.Data())
l.BlockNumber = newHexNum(log.Number())
return l
} }
func NewLogsRes(logs state.Logs) (ls []LogRes) { func NewLogsRes(logs state.Logs) (ls []LogRes) {
ls = make([]LogRes, len(logs)) ls = make([]LogRes, len(logs))
for i, log := range logs { for i, log := range logs {
var l LogRes ls[i] = NewLogRes(log)
l.Topics = make([]string, len(log.Topics()))
l.Address = log.Address().Hex()
l.Data = common.ToHex(log.Data())
l.Number = log.Number()
for j, topic := range log.Topics() {
l.Topics[j] = topic.Hex()
}
ls[i] = l
} }
return return

154
rpc/responses_test.go Normal file
View file

@ -0,0 +1,154 @@
package rpc
import (
"encoding/json"
"fmt"
"math/big"
"regexp"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
)
const (
reHash = `"0x[0-9a-f]{64}"` // 32 bytes
reHashOpt = `"(0x[0-9a-f]{64})"|null` // 32 bytes or null
reAddress = `"0x[0-9a-f]{40}"` // 20 bytes
reAddressOpt = `"0x[0-9a-f]{40}"|null` // 20 bytes or null
reNum = `"0x([1-9a-f][0-9a-f]{1,15})|0"` // must not have left-padded zeros
reData = `"0x[0-9a-f]*"` // can be "empty"
)
func TestNewBlockRes(t *testing.T) {
parentHash := common.HexToHash("0x01")
coinbase := common.HexToAddress("0x01")
root := common.HexToHash("0x01")
difficulty := common.Big1
nonce := uint64(1)
extra := ""
block := types.NewBlock(parentHash, coinbase, root, difficulty, nonce, extra)
tests := map[string]string{
"number": reNum,
"hash": reHash,
"parentHash": reHash,
"nonce": reNum,
"sha3Uncles": reHash,
"logsBloom": reData,
"transactionRoot": reHash,
"stateRoot": reHash,
"miner": reAddress,
"difficulty": `"0x1"`,
"totalDifficulty": reNum,
"size": reNum,
"extraData": reData,
"gasLimit": reNum,
// "minGasPrice": "0x",
"gasUsed": reNum,
"timestamp": reNum,
}
v := NewBlockRes(block, false)
j, _ := json.Marshal(v)
for k, re := range tests {
match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j))
if !match {
t.Error(fmt.Sprintf("%s output json does not match format %s. Got %s", k, re, j))
}
}
}
func TestNewTransactionRes(t *testing.T) {
to := common.HexToAddress("0x02")
amount := big.NewInt(1)
gasAmount := big.NewInt(1)
gasPrice := big.NewInt(1)
data := []byte{1, 2, 3}
tx := types.NewTransactionMessage(to, amount, gasAmount, gasPrice, data)
tests := map[string]string{
"hash": reHash,
"nonce": reNum,
"blockHash": reHash,
"blockNum": reNum,
"transactionIndex": reNum,
"from": reAddress,
"to": reAddressOpt,
"value": reNum,
"gas": reNum,
"gasPrice": reNum,
"input": reData,
}
v := NewTransactionRes(tx)
v.BlockHash = newHexData(common.HexToHash("0x030201"))
v.BlockNumber = newHexNum(5)
v.TxIndex = newHexNum(0)
j, _ := json.Marshal(v)
for k, re := range tests {
match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j))
if !match {
t.Error(fmt.Sprintf("`%s` output json does not match format %s. Source %s", k, re, j))
}
}
}
func TestNewLogRes(t *testing.T) {
log := makeStateLog(0)
tests := map[string]string{
"address": reAddress,
// "topics": "[.*]"
"data": reData,
"blockNumber": reNum,
// "hash": reHash,
// "logIndex": reNum,
// "blockHash": reHash,
// "transactionHash": reHash,
"transactionIndex": reNum,
}
v := NewLogRes(log)
j, _ := json.Marshal(v)
for k, re := range tests {
match, _ := regexp.MatchString(fmt.Sprintf(`{.*"%s":%s.*}`, k, re), string(j))
if !match {
t.Error(fmt.Sprintf("`%s` output json does not match format %s. Got %s", k, re, j))
}
}
}
func TestNewLogsRes(t *testing.T) {
logs := make([]state.Log, 3)
logs[0] = makeStateLog(1)
logs[1] = makeStateLog(2)
logs[2] = makeStateLog(3)
tests := map[string]string{}
v := NewLogsRes(logs)
j, _ := json.Marshal(v)
for k, re := range tests {
match, _ := regexp.MatchString(fmt.Sprintf(`[{.*"%s":%s.*}]`, k, re), string(j))
if !match {
t.Error(fmt.Sprintf("%s output json does not match format %s. Got %s", k, re, j))
}
}
}
func makeStateLog(num int) state.Log {
address := common.HexToAddress("0x0")
data := []byte{1, 2, 3}
number := uint64(num)
topics := make([]common.Hash, 3)
topics = append(topics, common.HexToHash("0x00"))
topics = append(topics, common.HexToHash("0x10"))
topics = append(topics, common.HexToHash("0x20"))
log := state.NewLog(address, topics, data, number)
return log
}

View file

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math/big" "math/big"
@ -69,7 +68,7 @@ type btBlock struct {
BlockHeader *btHeader BlockHeader *btHeader
Rlp string Rlp string
Transactions []btTransaction Transactions []btTransaction
UncleHeaders []string UncleHeaders []*btHeader
} }
type BlockTest struct { type BlockTest struct {
@ -106,21 +105,21 @@ func (t *BlockTest) InsertPreState(db common.Database) (*state.StateDB, error) {
balance, _ := new(big.Int).SetString(acct.Balance, 0) balance, _ := new(big.Int).SetString(acct.Balance, 0)
nonce, _ := strconv.ParseUint(acct.Nonce, 16, 64) nonce, _ := strconv.ParseUint(acct.Nonce, 16, 64)
obj := statedb.NewStateObject(common.HexToAddress(addrString)) obj := statedb.CreateAccount(common.HexToAddress(addrString))
obj.SetCode(code) obj.SetCode(code)
obj.SetBalance(balance) obj.SetBalance(balance)
obj.SetNonce(nonce) obj.SetNonce(nonce)
// for k, v := range acct.Storage { for k, v := range acct.Storage {
// obj.SetState(k, v) statedb.SetState(common.HexToAddress(addrString), common.HexToHash(k), common.FromHex(v))
// } }
} }
// sync objects to trie // sync objects to trie
statedb.Update(nil) statedb.Update()
// sync trie to disk // sync trie to disk
statedb.Sync() statedb.Sync()
if !bytes.Equal(t.Genesis.Root().Bytes(), statedb.Root().Bytes()) { if !bytes.Equal(t.Genesis.Root().Bytes(), statedb.Root().Bytes()) {
return nil, errors.New("computed state root does not match genesis block") return nil, fmt.Errorf("computed state root does not match genesis block %x %x", t.Genesis.Root().Bytes()[:4], statedb.Root().Bytes()[:4])
} }
return statedb, nil return statedb, nil
} }

View file

@ -1,5 +1,5 @@
{ {
"genesis_rlp_hex": "f90219f90214a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a09178d0f23c965d81f0834a4c72c6253ce6830f4022b1359aaebfc1ecba442d4ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080830f4240808080a00000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000088000000000000002ac0c0", "genesis_rlp_hex": "f901f8f901f3a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a09178d0f23c965d81f0834a4c72c6253ce6830f4022b1359aaebfc1ecba442d4ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080832fefd8808080a0000000000000000000000000000000000000000000000000000000000000000088000000000000002ac0c0",
"genesis_state_root": "9178d0f23c965d81f0834a4c72c6253ce6830f4022b1359aaebfc1ecba442d4e", "genesis_state_root": "9178d0f23c965d81f0834a4c72c6253ce6830f4022b1359aaebfc1ecba442d4e",
"genesis_hash": "b5d6d8402156c5c1dfadaa4b87c676b5bcadb17ef9bc8e939606daaa0d35f55d" "genesis_hash": "fd4af92a79c7fc2fd8bf0d342f2e832e1d4f485c85b9152d2039e03bc604fdca"
} }

View file

@ -9,5 +9,16 @@
"full_size": 1073739904, "full_size": 1073739904,
"header_hash": "2a8de2adf89af77358250bf908bf04ba94a6e8c3ba87775564a41d269a05e4ce", "header_hash": "2a8de2adf89af77358250bf908bf04ba94a6e8c3ba87775564a41d269a05e4ce",
"cache_hash": "35ded12eecf2ce2e8da2e15c06d463aae9b84cb2530a00b932e4bbc484cde353" "cache_hash": "35ded12eecf2ce2e8da2e15c06d463aae9b84cb2530a00b932e4bbc484cde353"
},
"second": {
"nonce": "307692cf71b12f6d",
"mixhash": "e55d02c555a7969361cf74a9ec6211d8c14e4517930a00442f171bdb1698d175",
"header": "f901f7a01bef91439a3e070a6586851c11e6fd79bbbea074b2b836727b8e75c7d4a6b698a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794ea3cb5f94fa2ddd52ec6dd6eb75cf824f4058ca1a00c6e51346be0670ce63ac5f05324e27d20b180146269c5aab844d09a2b108c64a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302004002832fefd880845511ed2a80a0e55d02c555a7969361cf74a9ec6211d8c14e4517930a00442f171bdb1698d17588307692cf71b12f6d",
"seed": "0000000000000000000000000000000000000000000000000000000000000000",
"result": "ab9b13423cface72cbec8424221651bc2e384ef0f7a560e038fc68c8d8684829",
"cache_size": 16776896,
"full_size": 1073739904,
"header_hash": "100cbec5e5ef82991290d0d93d758f19082e71f234cf479192a8b94df6da6bfe",
"cache_hash": "35ded12eecf2ce2e8da2e15c06d463aae9b84cb2530a00b932e4bbc484cde353"
} }
} }

View file

@ -0,0 +1,71 @@
{
"randomStatetest" : {
"env" : {
"currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5",
"currentDifficulty" : "5623894562375",
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"currentNumber" : "0",
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "972201916",
"code" : "0x7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000019e7f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000c35036017f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b51916f2620a6e7d187a5560005155",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "912450255",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "999999998115347875",
"code" : "0x",
"nonce" : "1",
"storage" : {
}
}
},
"postStateRoot" : "24e7dcfb7ff0269a9000bedfeafad33c3ccc3610e3031c88bace245f3cbe64d2",
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000019e7f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000c35036017f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b51916f2620a6e7d187a5560005155",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "46",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "0x7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000019e7f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000c35036017f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b51916f2620a6e7d187a",
"gasLimit" : "0x3662e2a1",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "972201916"
}
}
}

View file

@ -0,0 +1,71 @@
{
"randomStatetest" : {
"env" : {
"currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5",
"currentDifficulty" : "5623894562375",
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"currentNumber" : "0",
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x7f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b57f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000c350377f000000000000000000000000000000000000000000000000000000000000c3507f00000000000000000000000100000000000000000000000000000000000000000b3a09785b1084418866100af0868a3455",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "1556088597",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "999999998443911449",
"code" : "0x",
"nonce" : "1",
"storage" : {
}
}
},
"postStateRoot" : "886793cb795aad67bef8747046a71428c332fac253236d4002e142f08f31602b",
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x7f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b57f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000c350377f000000000000000000000000000000000000000000000000000000000000c3507f00000000000000000000000100000000000000000000000000000000000000000b3a09785b1084418866100af0868a3455",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "46",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "0x7f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b57f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000c350377f000000000000000000000000000000000000000000000000000000000000c3507f00000000000000000000000100000000000000000000000000000000000000000b3a09785b1084418866100af0868a34",
"gasLimit" : "0x5cc006e7",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "2056709657"
}
}
}

View file

@ -0,0 +1,71 @@
{
"randomStatetest" : {
"env" : {
"currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5",
"currentDifficulty" : "5623894562375",
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"currentNumber" : "0",
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000100000000000000000000000000000000000000004340427f000000000000000000000000000000000000000000000000000000000000c3507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff52835a546b6685923465",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "1241595343",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "999999998758404703",
"code" : "0x",
"nonce" : "1",
"storage" : {
}
}
},
"postStateRoot" : "adebc5b15e70b5b8ad3eb5d2d0bc7880149d6f86d96cbc861494f810c3d325c1",
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000100000000000000000000000000000000000000004340427f000000000000000000000000000000000000000000000000000000000000c3507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff52835a546b6685923465",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "46",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000100000000000000000000000000000000000000004340427f000000000000000000000000000000000000000000000000000000000000c3507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff52835a546b6685923465",
"gasLimit" : "0x4a013da1",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "1959890283"
}
}
}

View file

@ -0,0 +1,71 @@
{
"randomStatetest" : {
"env" : {
"currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5",
"currentDifficulty" : "5623894562375",
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"currentNumber" : "0",
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x7f000000000000000000000000000000000000000000000000000000000000c3507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f00000000000000000000000100000000000000000000000000000000000000007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe527f00000000000000000000000000000000000000000000000000000000000000017f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b5",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "1944132934",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "999999998055867112",
"code" : "0x",
"nonce" : "1",
"storage" : {
}
}
},
"postStateRoot" : "16d58f4edca99e12b53543966af5ef6159af7dbf93ef4f085bce2972ecb413ad",
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x7f000000000000000000000000000000000000000000000000000000000000c3507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f00000000000000000000000100000000000000000000000000000000000000007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe527f00000000000000000000000000000000000000000000000000000000000000017f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b5",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "46",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "0x7f000000000000000000000000000000000000000000000000000000000000c3507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f00000000000000000000000100000000000000000000000000000000000000007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe527f00000000000000000000000000000000000000000000000000000000000000017f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b5",
"gasLimit" : "0x73e11d18",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "1285310456"
}
}
}

View file

@ -0,0 +1,71 @@
{
"randomStatetest" : {
"env" : {
"currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5",
"currentDifficulty" : "5623894562375",
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"currentNumber" : "0",
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x7f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000027ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe307f00000000000000000000000100000000000000000000000000000000000000007f00000000000000000000000100000000000000000000000000000000000000005255",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "1380924181",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "999999998619075865",
"code" : "0x",
"nonce" : "1",
"storage" : {
}
}
},
"postStateRoot" : "224160a19d11a6252067a26ecfc2963fd1aff5c2b484a2f12c12ec7d5bbc547d",
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x7f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000027ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe307f00000000000000000000000100000000000000000000000000000000000000007f00000000000000000000000100000000000000000000000000000000000000005255",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "46",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "0x7f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000027ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe307f00000000000000000000000100000000000000000000000000000000000000007f000000000000000000000001000000000000000000000000000000000000000052",
"gasLimit" : "0x524f3ae7",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "936808044"
}
}
}

View file

@ -0,0 +1,71 @@
{
"randomStatetest" : {
"env" : {
"currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5",
"currentDifficulty" : "5623894562375",
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"currentNumber" : "0",
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x7f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b57f00000000000000000000000000000000000000000000000000000000000000017f00000000000000000000000100000000000000000000000000000000000000007f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001155933704",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "1932635520",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "999999998067364526",
"code" : "0x",
"nonce" : "1",
"storage" : {
}
}
},
"postStateRoot" : "3c9d47fb3ada07b78c2e6470ed8b4107db21e513979a71c270d97da5a20186bc",
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x7f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b57f00000000000000000000000000000000000000000000000000000000000000017f00000000000000000000000100000000000000000000000000000000000000007f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001155933704",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "46",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "0x7f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b57f00000000000000000000000000000000000000000000000000000000000000017f00000000000000000000000100000000000000000000000000000000000000007f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001155933704",
"gasLimit" : "0x7331ad52",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "1499936335"
}
}
}

View file

@ -0,0 +1,71 @@
{
"randomStatetest" : {
"env" : {
"currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5",
"currentDifficulty" : "5623894562375",
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"currentNumber" : "0",
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f000000000000000000000001000000000000000000000000000000000000000044457f0000000000000000000000000000000000000000000000000000000000000001187f00000000000000000000000000000000000000000000000000000000000000017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff52155955",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "1283993444",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "999999998716006602",
"code" : "0x",
"nonce" : "1",
"storage" : {
}
}
},
"postStateRoot" : "af423ed22996f4161da21729d08e3d9138d94234b778a043313bb6dcbc55d93d",
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f000000000000000000000001000000000000000000000000000000000000000044457f0000000000000000000000000000000000000000000000000000000000000001187f00000000000000000000000000000000000000000000000000000000000000017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff52155955",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "46",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f000000000000000000000001000000000000000000000000000000000000000044457f0000000000000000000000000000000000000000000000000000000000000001187f00000000000000000000000000000000000000000000000000000000000000017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff521559",
"gasLimit" : "0x4c882f36",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "679746513"
}
}
}

View file

@ -0,0 +1,71 @@
{
"randomStatetest" : {
"env" : {
"currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5",
"currentDifficulty" : "5623894562375",
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"currentNumber" : "0",
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x31417f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000c3507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f00000000000000000000000100000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0315208a675944747f7430661519049a55",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "704316238",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "999999999295683808",
"code" : "0x",
"nonce" : "1",
"storage" : {
}
}
},
"postStateRoot" : "ddb8f27abb51685caa793be133df9db3912648c02498b74a0ae874294acce6f0",
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x31417f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000c3507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f00000000000000000000000100000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0315208a675944747f7430661519049a55",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "46",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "0x31417f000000000000000000000000ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000c3507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f00000000000000000000000100000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0315208a675944747f7430661519049a",
"gasLimit" : "0x29fb0320",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "1758540724"
}
}
}

View file

@ -0,0 +1,71 @@
{
"randomStatetest" : {
"env" : {
"currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5",
"currentDifficulty" : "5623894562375",
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"currentNumber" : "0",
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x44207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f00000000000000000000000100000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000001145344846604627f5560005155",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "620442430",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "999999999379557616",
"code" : "0x",
"nonce" : "1",
"storage" : {
}
}
},
"postStateRoot" : "da70f5af0d1545ec9f0f0e28a55d3f2896bb3d64c71b4908f72ed26c345aafe4",
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x44207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f00000000000000000000000100000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000001145344846604627f5560005155",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "46",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "0x44207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7f00000000000000000000000100000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000945304eb96065b2a98b57a48a06ae28d285a71b57fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000001145344846604627f",
"gasLimit" : "0x24fb3310",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "1561176030"
}
}
}

View file

@ -0,0 +1,71 @@
{
"randomStatetest" : {
"env" : {
"currentCoinbase" : "945304eb96065b2a98b57a48a06ae28d285a71b5",
"currentDifficulty" : "5623894562375",
"currentGasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"currentNumber" : "0",
"currentTimestamp" : "1",
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x",
"post" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x7f0000000000000000000000000000000000000000000000000000000000000001207f000000000000000000000000000000000000000000000000000000000000c3507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000c3507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe406f",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "1258533548",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "999999998741466498",
"code" : "0x",
"nonce" : "1",
"storage" : {
}
}
},
"postStateRoot" : "e1881131f80068947922f01c78464f93c46e6e11314d0deceb55809e594aec0a",
"pre" : {
"095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
"balance" : "0",
"code" : "0x7f0000000000000000000000000000000000000000000000000000000000000001207f000000000000000000000000000000000000000000000000000000000000c3507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000c3507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe406f",
"nonce" : "0",
"storage" : {
}
},
"945304eb96065b2a98b57a48a06ae28d285a71b5" : {
"balance" : "46",
"code" : "0x6000355415600957005b60203560003555",
"nonce" : "0",
"storage" : {
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "1000000000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "0x7f0000000000000000000000000000000000000000000000000000000000000001207f000000000000000000000000000000000000000000000000000000000000c3507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000c3507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe406f",
"gasLimit" : "0x4b03b27e",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value" : "781711523"
}
}
}

View file

@ -847,6 +847,78 @@
"value" : "100000" "value" : "100000"
} }
}, },
"createJS_ExampleContract" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "256",
"currentGasLimit" : "1000000",
"currentNumber" : "0",
"currentTimestamp" : 1,
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"logs" : [
],
"out" : "0x60003560e060020a9004806343d726d61461004257806391b7f5ed14610050578063d686f9ee14610061578063f5bade661461006f578063fcfff16f1461008057005b61004a6101de565b60006000f35b61005b6004356100bf565b60006000f35b610069610304565b60006000f35b61007a60043561008e565b60006000f35b6100886100f0565b60006000f35b600054600160a060020a031633600160a060020a031614156100af576100b4565b6100bc565b806001819055505b50565b600054600160a060020a031633600160a060020a031614156100e0576100e5565b6100ed565b806002819055505b50565b600054600160a060020a031633600160a060020a031614806101255750600354600160a060020a031633600160a060020a0316145b61012e57610161565b60016004819055507f59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a560006000a16101dc565b60045460011480610173575060015434105b6101b85760016004819055507f59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a560006000a142600581905550336003819055506101db565b33600160a060020a03166000346000600060006000848787f16101d757005b5050505b5b565b60006004546000146101ef576101f4565b610301565b600054600160a060020a031633600160a060020a031614801561022c5750600054600160a060020a0316600354600160a060020a0316145b61023557610242565b6000600481905550610301565b600354600160a060020a031633600160a060020a03161461026257610300565b600554420360025402905060015481116102c757600354600160a060020a0316600082600154036000600060006000848787f161029b57005b505050600054600160a060020a03166000826000600060006000848787f16102bf57005b5050506102ee565b600054600160a060020a031660006001546000600060006000848787f16102ea57005b5050505b60006004819055506000546003819055505b5b50565b6000600054600160a060020a031633600160a060020a031614156103275761032c565b61037e565b600554420360025402905060015481116103455761037d565b600054600160a060020a031660006001546000600060006000848787f161036857005b50505060006004819055506000546003819055505b5b5056",
"post" : {
"2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : {
"balance" : "366356",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
},
"6295ee1b4f6dd65047762f924ecd367c17eabf8f" : {
"balance" : "200000",
"code" : "0x60003560e060020a9004806343d726d61461004257806391b7f5ed14610050578063d686f9ee14610061578063f5bade661461006f578063fcfff16f1461008057005b61004a6101de565b60006000f35b61005b6004356100bf565b60006000f35b610069610304565b60006000f35b61007a60043561008e565b60006000f35b6100886100f0565b60006000f35b600054600160a060020a031633600160a060020a031614156100af576100b4565b6100bc565b806001819055505b50565b600054600160a060020a031633600160a060020a031614156100e0576100e5565b6100ed565b806002819055505b50565b600054600160a060020a031633600160a060020a031614806101255750600354600160a060020a031633600160a060020a0316145b61012e57610161565b60016004819055507f59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a560006000a16101dc565b60045460011480610173575060015434105b6101b85760016004819055507f59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a560006000a142600581905550336003819055506101db565b33600160a060020a03166000346000600060006000848787f16101d757005b5050505b5b565b60006004546000146101ef576101f4565b610301565b600054600160a060020a031633600160a060020a031614801561022c5750600054600160a060020a0316600354600160a060020a0316145b61023557610242565b6000600481905550610301565b600354600160a060020a031633600160a060020a03161461026257610300565b600554420360025402905060015481116102c757600354600160a060020a0316600082600154036000600060006000848787f161029b57005b505050600054600160a060020a03166000826000600060006000848787f16102bf57005b5050506102ee565b600054600160a060020a031660006001546000600060006000848787f16102ea57005b5050505b60006004819055506000546003819055505b5b50565b6000600054600160a060020a031633600160a060020a031614156103275761032c565b61037e565b600554420360025402905060015481116103455761037d565b600054600160a060020a031660006001546000600060006000848787f161036857005b50505060006004819055506000546003819055505b5b5056",
"nonce" : "0",
"storage" : {
"0x" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"0x01" : "0x42",
"0x02" : "0x23",
"0x03" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"0x05" : "0x01"
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "9999999533644",
"code" : "0x",
"nonce" : "1",
"storage" : {
}
}
},
"postStateRoot" : "5500215cdbf8165ca47720ad088ae49c2441560cdf267f1c946ae7b9807cb1d4",
"pre" : {
"6295ee1b4f6dd65047762f924ecd367c17eabf8f" : {
"balance" : "100000",
"code" : "0x60003560e060020a9004806343d726d61461004257806391b7f5ed14610050578063d686f9ee14610061578063f5bade661461006f578063fcfff16f1461008057005b61004a6101de565b60006000f35b61005b6004356100bf565b60006000f35b610069610304565b60006000f35b61007a60043561008e565b60006000f35b6100886100f0565b60006000f35b600054600160a060020a031633600160a060020a031614156100af576100b4565b6100bc565b806001819055505b50565b600054600160a060020a031633600160a060020a031614156100e0576100e5565b6100ed565b806002819055505b50565b600054600160a060020a031633600160a060020a031614806101255750600354600160a060020a031633600160a060020a0316145b61012e57610161565b60016004819055507f59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a560006000a16101dc565b60045460011480610173575060015434105b6101b85760016004819055507f59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a560006000a142600581905550336003819055506101db565b33600160a060020a03166000346000600060006000848787f16101d757005b5050505b5b565b60006004546000146101ef576101f4565b610301565b600054600160a060020a031633600160a060020a031614801561022c5750600054600160a060020a0316600354600160a060020a0316145b61023557610242565b6000600481905550610301565b600354600160a060020a031633600160a060020a03161461026257610300565b600554420360025402905060015481116102c757600354600160a060020a0316600082600154036000600060006000848787f161029b57005b505050600054600160a060020a03166000826000600060006000848787f16102bf57005b5050506102ee565b600054600160a060020a031660006001546000600060006000848787f16102ea57005b5050505b60006004819055506000546003819055505b5b50565b6000600054600160a060020a031633600160a060020a031614156103275761032c565b61037e565b600554420360025402905060015481116103455761037d565b600054600160a060020a031660006001546000600060006000848787f161036857005b50505060006004819055506000546003819055505b5b5056",
"nonce" : "0",
"storage" : {
"0x" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"0x01" : "0x42",
"0x02" : "0x23",
"0x03" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"0x05" : "0x54c98c81"
}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "10000000000000",
"code" : "0x",
"nonce" : "0",
"storage" : {
}
}
},
"transaction" : {
"data" : "0x60406103ca600439600451602451336000819055506000600481905550816001819055508060028190555042600581905550336003819055505050610381806100496000396000f30060003560e060020a9004806343d726d61461004257806391b7f5ed14610050578063d686f9ee14610061578063f5bade661461006f578063fcfff16f1461008057005b61004a6101de565b60006000f35b61005b6004356100bf565b60006000f35b610069610304565b60006000f35b61007a60043561008e565b60006000f35b6100886100f0565b60006000f35b600054600160a060020a031633600160a060020a031614156100af576100b4565b6100bc565b806001819055505b50565b600054600160a060020a031633600160a060020a031614156100e0576100e5565b6100ed565b806002819055505b50565b600054600160a060020a031633600160a060020a031614806101255750600354600160a060020a031633600160a060020a0316145b61012e57610161565b60016004819055507f59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a560006000a16101dc565b60045460011480610173575060015434105b6101b85760016004819055507f59ebeb90bc63057b6515673c3ecf9438e5058bca0f92585014eced636878c9a560006000a142600581905550336003819055506101db565b33600160a060020a03166000346000600060006000848787f16101d757005b5050505b5b565b60006004546000146101ef576101f4565b610301565b600054600160a060020a031633600160a060020a031614801561022c5750600054600160a060020a0316600354600160a060020a0316145b61023557610242565b6000600481905550610301565b600354600160a060020a031633600160a060020a03161461026257610300565b600554420360025402905060015481116102c757600354600160a060020a0316600082600154036000600060006000848787f161029b57005b505050600054600160a060020a03166000826000600060006000848787f16102bf57005b5050506102ee565b600054600160a060020a031660006001546000600060006000848787f16102ea57005b5050505b60006004819055506000546003819055505b5b50565b6000600054600160a060020a031633600160a060020a031614156103275761032c565b61037e565b600554420360025402905060015481116103455761037d565b600054600160a060020a031660006001546000600060006000848787f161036857005b50505060006004819055506000546003819055505b5b505600000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000023",
"gasLimit" : "600000",
"gasPrice" : "1",
"nonce" : "0",
"secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"to" : "",
"value" : "100000"
}
},
"createNameRegistratorendowmentTooHigh" : { "createNameRegistratorendowmentTooHigh" : {
"env" : { "env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",

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