mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-22 12:46:44 +00:00
Merge remote-tracking branch 'upstream/develop' into bzz
This commit is contained in:
commit
77d7ef927c
107 changed files with 6412 additions and 1852 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -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
3
.gitmodules
vendored
|
|
@ -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
|
||||||
|
|
|
||||||
2
.mailmap
2
.mailmap
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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 .
|
||||||
|
|
|
||||||
|
|
@ -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
4
Godeps/Godeps.json
generated
|
|
@ -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
4
Godeps/_workspace/src/github.com/rs/cors/.travis.yml
generated
vendored
Normal 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
19
Godeps/_workspace/src/github.com/rs/cors/LICENSE
generated
vendored
Normal 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
84
Godeps/_workspace/src/github.com/rs/cors/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
# Go CORS handler [](https://godoc.org/github.com/rs/cors) [](https://raw.githubusercontent.com/rs/cors/master/LICENSE) [](https://travis-ci.org/rs/cors)
|
||||||
|
|
||||||
|
CORS is a `net/http` handler implementing [Cross Origin Resource Sharing W3 specification](http://www.w3.org/TR/cors/) in Golang.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
After installing Go and setting up your [GOPATH](http://golang.org/doc/code.html#GOPATH), create your first `.go` file. We'll call it `server.go`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/rs/cors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Write([]byte("{\"hello\": \"world\"}"))
|
||||||
|
})
|
||||||
|
|
||||||
|
// cors.Default() setup the middleware with default options being
|
||||||
|
// all origins accepted with simple methods (GET, POST). See
|
||||||
|
// documentation below for more options.
|
||||||
|
handler = cors.Default().Handler(h)
|
||||||
|
http.ListenAndServe(":8080", handler)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Install `cors`:
|
||||||
|
|
||||||
|
go get github.com/rs/cors
|
||||||
|
|
||||||
|
Then run your server:
|
||||||
|
|
||||||
|
go run server.go
|
||||||
|
|
||||||
|
The server now runs on `localhost:8080`:
|
||||||
|
|
||||||
|
$ curl -D - -H 'Origin: http://foo.com' http://localhost:8080/
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Access-Control-Allow-Origin: foo.com
|
||||||
|
Content-Type: application/json
|
||||||
|
Date: Sat, 25 Oct 2014 03:43:57 GMT
|
||||||
|
Content-Length: 18
|
||||||
|
|
||||||
|
{"hello": "world"}
|
||||||
|
|
||||||
|
### More Examples
|
||||||
|
|
||||||
|
* `net/http`: [examples/nethttp/server.go](https://github.com/rs/cors/blob/master/examples/nethttp/server.go)
|
||||||
|
* [Goji](https://goji.io): [examples/goji/server.go](https://github.com/rs/cors/blob/master/examples/goji/server.go)
|
||||||
|
* [Martini](http://martini.codegangsta.io): [examples/martini/server.go](https://github.com/rs/cors/blob/master/examples/martini/server.go)
|
||||||
|
* [Negroni](https://github.com/codegangsta/negroni): [examples/negroni/server.go](https://github.com/rs/cors/blob/master/examples/negroni/server.go)
|
||||||
|
* [Alice](https://github.com/justinas/alice): [examples/alice/server.go](https://github.com/rs/cors/blob/master/examples/alice/server.go)
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
Parameters are passed to the middleware thru the `cors.New` method as follow:
|
||||||
|
|
||||||
|
```go
|
||||||
|
c := cors.New(cors.Options{
|
||||||
|
AllowedOrigins: []string{"http://foo.com"},
|
||||||
|
AllowCredentials: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Insert the middleware
|
||||||
|
handler = c.Handler(handler)
|
||||||
|
```
|
||||||
|
|
||||||
|
* **AllowedOrigins** `[]string`: A list of origins a cross-domain request can be executed from. If the special `*` value is present in the list, all origins will be allowed. The default value is `*`.
|
||||||
|
* **AllowedMethods** `[]string`: A list of methods the client is allowed to use with cross-domain requests.
|
||||||
|
* **AllowedHeaders** `[]string`: A list of non simple headers the client is allowed to use with cross-domain requests. Default value is simple methods (`GET` and `POST`)
|
||||||
|
* **ExposedHeaders** `[]string`: Indicates which headers are safe to expose to the API of a CORS API specification
|
||||||
|
* **AllowCredentials** `bool`: Indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates. The default is `false`.
|
||||||
|
* **MaxAge** `int`: Indicates how long (in seconds) the results of a preflight request can be cached. The default is `0` which stands for no max age.
|
||||||
|
|
||||||
|
See [API documentation](http://godoc.org/github.com/rs/cors) for more info.
|
||||||
|
|
||||||
|
## Licenses
|
||||||
|
|
||||||
|
All source code is licensed under the [MIT License](https://raw.github.com/rs/cors/master/LICENSE).
|
||||||
37
Godeps/_workspace/src/github.com/rs/cors/bench_test.go
generated
vendored
Normal file
37
Godeps/_workspace/src/github.com/rs/cors/bench_test.go
generated
vendored
Normal 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
308
Godeps/_workspace/src/github.com/rs/cors/cors.go
generated
vendored
Normal 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
288
Godeps/_workspace/src/github.com/rs/cors/cors_test.go
generated
vendored
Normal 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": "",
|
||||||
|
})
|
||||||
|
}
|
||||||
24
Godeps/_workspace/src/github.com/rs/cors/examples/alice/server.go
generated
vendored
Normal file
24
Godeps/_workspace/src/github.com/rs/cors/examples/alice/server.go
generated
vendored
Normal 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)
|
||||||
|
}
|
||||||
18
Godeps/_workspace/src/github.com/rs/cors/examples/default/server.go
generated
vendored
Normal file
18
Godeps/_workspace/src/github.com/rs/cors/examples/default/server.go
generated
vendored
Normal 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)
|
||||||
|
}
|
||||||
22
Godeps/_workspace/src/github.com/rs/cors/examples/goji/server.go
generated
vendored
Normal file
22
Godeps/_workspace/src/github.com/rs/cors/examples/goji/server.go
generated
vendored
Normal 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()
|
||||||
|
}
|
||||||
23
Godeps/_workspace/src/github.com/rs/cors/examples/martini/server.go
generated
vendored
Normal file
23
Godeps/_workspace/src/github.com/rs/cors/examples/martini/server.go
generated
vendored
Normal 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()
|
||||||
|
}
|
||||||
26
Godeps/_workspace/src/github.com/rs/cors/examples/negroni/server.go
generated
vendored
Normal file
26
Godeps/_workspace/src/github.com/rs/cors/examples/negroni/server.go
generated
vendored
Normal 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")
|
||||||
|
}
|
||||||
20
Godeps/_workspace/src/github.com/rs/cors/examples/nethttp/server.go
generated
vendored
Normal file
20
Godeps/_workspace/src/github.com/rs/cors/examples/nethttp/server.go
generated
vendored
Normal 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))
|
||||||
|
}
|
||||||
22
Godeps/_workspace/src/github.com/rs/cors/examples/openbar/server.go
generated
vendored
Normal file
22
Godeps/_workspace/src/github.com/rs/cors/examples/openbar/server.go
generated
vendored
Normal 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
27
Godeps/_workspace/src/github.com/rs/cors/utils.go
generated
vendored
Normal 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
28
Godeps/_workspace/src/github.com/rs/cors/utils_test.go
generated
vendored
Normal 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
18
README.md
18
README.md
|
|
@ -4,8 +4,8 @@ Ethereum Go Client © 2014 Jeffrey Wilcke.
|
||||||
|
|
||||||
| Linux | OSX | Windows | Tests
|
| Linux | OSX | Windows | Tests
|
||||||
----------|---------|-----|---------|------
|
----------|---------|-----|---------|------
|
||||||
develop | [](https://build.ethdev.com/builders/Linux%20Go%20develop%20branch/builds/-1) | [](https://build.ethdev.com/builders/OSX%20Go%20develop%20branch/builds/-1) | N/A | [](https://travis-ci.org/ethereum/go-ethereum)
|
develop | [](https://build.ethdev.com/builders/Linux%20Go%20develop%20branch/builds/-1) | [](https://build.ethdev.com/builders/OSX%20Go%20develop%20branch/builds/-1) | N/A | [](https://travis-ci.org/ethereum/go-ethereum) [](https://coveralls.io/r/ethereum/go-ethereum?branch=develop)
|
||||||
master | [](https://build.ethdev.com/builders/Linux%20Go%20master%20branch/builds/-1) | [](https://build.ethdev.com/builders/OSX%20Go%20master%20branch/builds/-1) | N/A | [](https://travis-ci.org/ethereum/go-ethereum)
|
master | [](https://build.ethdev.com/builders/Linux%20Go%20master%20branch/builds/-1) | [](https://build.ethdev.com/builders/OSX%20Go%20master%20branch/builds/-1) | N/A | [](https://travis-ci.org/ethereum/go-ethereum) [](https://coveralls.io/r/ethereum/go-ethereum?branch=master)
|
||||||
|
|
||||||
[](https://waffle.io/ethereum/go-ethereum)
|
[](https://waffle.io/ethereum/go-ethereum)
|
||||||
[](https://waffle.io/ethereum/go-ethereum)
|
[](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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
@ -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(ð.Config{
|
ethereum, err = eth.New(ð.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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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{})
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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{})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
200
core/vm/gas.go
200
core/vm/gas.go
|
|
@ -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},
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
160
core/vm/vm.go
160
core/vm/vm.go
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 }
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
65
generators/defaults.go
Normal 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
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
54
params/protocol_params.go
Executable 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.
|
||||||
|
)
|
||||||
166
rpc/api.go
166
rpc/api.go
|
|
@ -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
|
|
||||||
*/
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
552
rpc/args.go
552
rpc/args.go
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1623
rpc/args_test.go
1623
rpc/args_test.go
File diff suppressed because it is too large
Load diff
36
rpc/http.go
36
rpc/http.go
|
|
@ -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:
|
||||||
|
|
|
||||||
109
rpc/messages.go
109
rpc/messages.go
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
309
rpc/responses.go
309
rpc/responses.go
|
|
@ -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
154
rpc/responses_test.go
Normal 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
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
71
tests/files/StateTests/RandomTests/st201503261401CPPJIT.json
Normal file
71
tests/files/StateTests/RandomTests/st201503261401CPPJIT.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
tests/files/StateTests/RandomTests/st201503302200JS.json
Normal file
71
tests/files/StateTests/RandomTests/st201503302200JS.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
tests/files/StateTests/RandomTests/st201503302202JS.json
Normal file
71
tests/files/StateTests/RandomTests/st201503302202JS.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
tests/files/StateTests/RandomTests/st201503302206JS.json
Normal file
71
tests/files/StateTests/RandomTests/st201503302206JS.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
tests/files/StateTests/RandomTests/st201503302208JS.json
Normal file
71
tests/files/StateTests/RandomTests/st201503302208JS.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
tests/files/StateTests/RandomTests/st201503302210JS.json
Normal file
71
tests/files/StateTests/RandomTests/st201503302210JS.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
tests/files/StateTests/RandomTests/st201503302211JS.json
Normal file
71
tests/files/StateTests/RandomTests/st201503302211JS.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
tests/files/StateTests/RandomTests/st201504011535GO.json
Normal file
71
tests/files/StateTests/RandomTests/st201504011535GO.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
tests/files/StateTests/RandomTests/st201504011536GO.json
Normal file
71
tests/files/StateTests/RandomTests/st201504011536GO.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
tests/files/StateTests/RandomTests/st201504011547GO.json
Normal file
71
tests/files/StateTests/RandomTests/st201504011547GO.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
Loading…
Reference in a new issue