This commit is contained in:
iamweird5 2018-03-09 20:06:43 +00:00 committed by GitHub
commit d960dcb6a3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 9436 additions and 10271 deletions

3
.gitignore vendored
View file

@ -42,3 +42,6 @@ profile.cov
/dashboard/assets/node_modules /dashboard/assets/node_modules
/dashboard/assets/stats.json /dashboard/assets/stats.json
/dashboard/assets/bundle.js /dashboard/assets/bundle.js
/dashboard/assets/package-lock.json
**/yarn-error.log

View file

@ -14,7 +14,6 @@ matrix:
- go run build/ci.go install - go run build/ci.go install
- go run build/ci.go test -coverage - go run build/ci.go test -coverage
# These are the latest Go versions.
- os: linux - os: linux
dist: trusty dist: trusty
sudo: required sudo: required
@ -26,8 +25,20 @@ matrix:
- go run build/ci.go install - go run build/ci.go install
- go run build/ci.go test -coverage - go run build/ci.go test -coverage
# These are the latest Go versions.
- os: linux
dist: trusty
sudo: required
go: "1.10"
script:
- sudo modprobe fuse
- sudo chmod 666 /dev/fuse
- sudo chown root:$USER /etc/fuse.conf
- go run build/ci.go install
- go run build/ci.go test -coverage
- os: osx - os: osx
go: 1.9.x go: "1.10"
script: script:
- unset -f cd # workaround for https://github.com/travis-ci/travis-ci/issues/8703 - unset -f cd # workaround for https://github.com/travis-ci/travis-ci/issues/8703
- brew update - brew update
@ -39,7 +50,7 @@ matrix:
# This builder only tests code linters on latest version of Go # This builder only tests code linters on latest version of Go
- os: linux - os: linux
dist: trusty dist: trusty
go: 1.9.x go: "1.10"
env: env:
- lint - lint
git: git:
@ -51,7 +62,7 @@ matrix:
- os: linux - os: linux
dist: trusty dist: trusty
sudo: required sudo: required
go: 1.9.x go: "1.10"
env: env:
- ubuntu-ppa - ubuntu-ppa
- azure-linux - azure-linux
@ -91,7 +102,7 @@ matrix:
dist: trusty dist: trusty
services: services:
- docker - docker
go: 1.9.x go: "1.10"
env: env:
- azure-linux-mips - azure-linux-mips
git: git:
@ -135,7 +146,7 @@ matrix:
git: git:
submodules: false # avoid cloning ethereum/tests submodules: false # avoid cloning ethereum/tests
before_install: before_install:
- curl https://storage.googleapis.com/golang/go1.9.2.linux-amd64.tar.gz | tar -xz - curl https://storage.googleapis.com/golang/go1.10.linux-amd64.tar.gz | tar -xz
- export PATH=`pwd`/go/bin:$PATH - export PATH=`pwd`/go/bin:$PATH
- export GOROOT=`pwd`/go - export GOROOT=`pwd`/go
- export GOPATH=$HOME/go - export GOPATH=$HOME/go
@ -152,7 +163,7 @@ matrix:
# This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads # This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads
- os: osx - os: osx
go: 1.9.x go: "1.10"
env: env:
- azure-osx - azure-osx
- azure-ios - azure-ios
@ -182,7 +193,7 @@ matrix:
- os: linux - os: linux
dist: trusty dist: trusty
sudo: required sudo: required
go: 1.9.x go: "1.10"
env: env:
- azure-purge - azure-purge
git: git:

View file

@ -1,5 +1,5 @@
# Build Geth in a stock Go builder container # Build Geth in a stock Go builder container
FROM golang:1.9-alpine as builder FROM golang:1.10-alpine as builder
RUN apk add --no-cache make gcc musl-dev linux-headers RUN apk add --no-cache make gcc musl-dev linux-headers

View file

@ -1,5 +1,5 @@
# Build Geth in a stock Go builder container # Build Geth in a stock Go builder container
FROM golang:1.9-alpine as builder FROM golang:1.10-alpine as builder
RUN apk add --no-cache make gcc musl-dev linux-headers RUN apk add --no-cache make gcc musl-dev linux-headers

View file

@ -1 +1 @@
1.8.2 1.8.3

View file

@ -385,8 +385,7 @@ var methodNormalizer = map[Lang]func(string) string{
LangJava: decapitalise, LangJava: decapitalise,
} }
// capitalise makes the first character of a string upper case, also removing any // capitalise makes a camel-case string which starts with an upper case character.
// prefixing underscores from the variable names.
func capitalise(input string) string { func capitalise(input string) string {
for len(input) > 0 && input[0] == '_' { for len(input) > 0 && input[0] == '_' {
input = input[1:] input = input[1:]
@ -394,12 +393,42 @@ func capitalise(input string) string {
if len(input) == 0 { if len(input) == 0 {
return "" return ""
} }
return strings.ToUpper(input[:1]) + input[1:] return toCamelCase(strings.ToUpper(input[:1]) + input[1:])
} }
// decapitalise makes the first character of a string lower case. // decapitalise makes a camel-case string which starts with a lower case character.
func decapitalise(input string) string { func decapitalise(input string) string {
return strings.ToLower(input[:1]) + input[1:] for len(input) > 0 && input[0] == '_' {
input = input[1:]
}
if len(input) == 0 {
return ""
}
return toCamelCase(strings.ToLower(input[:1]) + input[1:])
}
// toCamelCase converts an under-score string to a camel-case string
func toCamelCase(input string) string {
toupper := false
result := ""
for k, v := range input {
switch {
case k == 0:
result = strings.ToUpper(string(input[0]))
case toupper:
result += strings.ToUpper(string(v))
toupper = false
case v == '_':
toupper = true
default:
result += string(v)
}
}
return result
} }
// structured checks whether a list of ABI data types has enough information to // structured checks whether a list of ABI data types has enough information to

View file

@ -425,7 +425,7 @@ var bindTests = []struct {
} }
`, `,
}, },
// Tests that gas estimation works for contracts with weird gas mechanics too. // Tests that gas estimation works for contracts with weird gas mechanics too.
{ {
`FunkyGasPattern`, `FunkyGasPattern`,
` `
@ -506,6 +506,7 @@ var bindTests = []struct {
} }
`, `,
}, },
// Tests that methods and returns with underscores inside work correctly.
{ {
`Underscorer`, `Underscorer`,
` `
@ -518,7 +519,7 @@ var bindTests = []struct {
} }
function LowerUpperCollision() constant returns (int _res, int Res) { function LowerUpperCollision() constant returns (int _res, int Res) {
return (1, 2); return (1, 2);
} }
function UpperLowerCollision() constant returns (int _Res, int res) { function UpperLowerCollision() constant returns (int _Res, int res) {
return (1, 2); return (1, 2);
} }
@ -531,9 +532,12 @@ var bindTests = []struct {
function AllPurelyUnderscoredOutput() constant returns (int _, int __) { function AllPurelyUnderscoredOutput() constant returns (int _, int __) {
return (1, 2); return (1, 2);
} }
function _under_scored_func() constant returns (int _int) {
return 0;
}
} }
`, `6060604052341561000f57600080fd5b6103498061001e6000396000f300606060405260043610610083576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806303a592131461008857806367e6633d146100b85780639df484851461014d578063af7486ab1461017d578063b564b34d146101ad578063e02ab24d146101dd578063e409ca451461020d575b600080fd5b341561009357600080fd5b61009b61023d565b604051808381526020018281526020019250505060405180910390f35b34156100c357600080fd5b6100cb610252565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b838110156101115780820151818401526020810190506100f6565b50505050905090810190601f16801561013e5780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b341561015857600080fd5b6101606102a0565b604051808381526020018281526020019250505060405180910390f35b341561018857600080fd5b6101906102b5565b604051808381526020018281526020019250505060405180910390f35b34156101b857600080fd5b6101c06102ca565b604051808381526020018281526020019250505060405180910390f35b34156101e857600080fd5b6101f06102df565b604051808381526020018281526020019250505060405180910390f35b341561021857600080fd5b6102206102f4565b604051808381526020018281526020019250505060405180910390f35b60008060016002819150809050915091509091565b600061025c610309565b61013a8090506040805190810160405280600281526020017f7069000000000000000000000000000000000000000000000000000000000000815250915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b6020604051908101604052806000815250905600a165627a7a72305820c11dcfa136fc7d182ee4d34f0b12d988496228f7e2d02d2b5376d996ca1743d00029`, `, `6060604052341561000f57600080fd5b6103858061001e6000396000f30060606040526004361061008e576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806303a592131461009357806346546dbe146100c357806367e6633d146100ec5780639df4848514610181578063af7486ab146101b1578063b564b34d146101e1578063e02ab24d14610211578063e409ca4514610241575b600080fd5b341561009e57600080fd5b6100a6610271565b604051808381526020018281526020019250505060405180910390f35b34156100ce57600080fd5b6100d6610286565b6040518082815260200191505060405180910390f35b34156100f757600080fd5b6100ff61028e565b6040518083815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561014557808201518184015260208101905061012a565b50505050905090810190601f1680156101725780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b341561018c57600080fd5b6101946102dc565b604051808381526020018281526020019250505060405180910390f35b34156101bc57600080fd5b6101c46102f1565b604051808381526020018281526020019250505060405180910390f35b34156101ec57600080fd5b6101f4610306565b604051808381526020018281526020019250505060405180910390f35b341561021c57600080fd5b61022461031b565b604051808381526020018281526020019250505060405180910390f35b341561024c57600080fd5b610254610330565b604051808381526020018281526020019250505060405180910390f35b60008060016002819150809050915091509091565b600080905090565b6000610298610345565b61013a8090506040805190810160405280600281526020017f7069000000000000000000000000000000000000000000000000000000000000815250915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b60008060016002819150809050915091509091565b6020604051908101604052806000815250905600a165627a7a72305820d1a53d9de9d1e3d55cb3dc591900b63c4f1ded79114f7b79b332684840e186a40029`,
`[{"constant":true,"inputs":[],"name":"LowerUpperCollision","outputs":[{"name":"_res","type":"int256"},{"name":"Res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UnderscoredOutput","outputs":[{"name":"_int","type":"int256"},{"name":"_string","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"PurelyUnderscoredOutput","outputs":[{"name":"_","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UpperLowerCollision","outputs":[{"name":"_Res","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"AllPurelyUnderscoredOutput","outputs":[{"name":"_","type":"int256"},{"name":"__","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UpperUpperCollision","outputs":[{"name":"_Res","type":"int256"},{"name":"Res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"LowerLowerCollision","outputs":[{"name":"_res","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"}]`, `[{"constant":true,"inputs":[],"name":"LowerUpperCollision","outputs":[{"name":"_res","type":"int256"},{"name":"Res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_under_scored_func","outputs":[{"name":"_int","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UnderscoredOutput","outputs":[{"name":"_int","type":"int256"},{"name":"_string","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"PurelyUnderscoredOutput","outputs":[{"name":"_","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UpperLowerCollision","outputs":[{"name":"_Res","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"AllPurelyUnderscoredOutput","outputs":[{"name":"_","type":"int256"},{"name":"__","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"UpperUpperCollision","outputs":[{"name":"_Res","type":"int256"},{"name":"Res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"LowerLowerCollision","outputs":[{"name":"_res","type":"int256"},{"name":"res","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"}]`,
` `
// Generate a new random account and a funded simulator // Generate a new random account and a funded simulator
key, _ := crypto.GenerateKey() key, _ := crypto.GenerateKey()
@ -562,6 +566,7 @@ var bindTests = []struct {
a, b, _ = underscorer.UpperUpperCollision(nil) a, b, _ = underscorer.UpperUpperCollision(nil)
a, b, _ = underscorer.PurelyUnderscoredOutput(nil) a, b, _ = underscorer.PurelyUnderscoredOutput(nil)
a, b, _ = underscorer.AllPurelyUnderscoredOutput(nil) a, b, _ = underscorer.AllPurelyUnderscoredOutput(nil)
a, _ = underscorer.UnderScoredFunc(nil)
fmt.Println(a, b, err) fmt.Println(a, b, err)
`, `,
@ -801,7 +806,8 @@ var bindTests = []struct {
// (See accounts/abi/unpack_test.go for more extensive testing) // (See accounts/abi/unpack_test.go for more extensive testing)
if retrievedArr[4][3][2] != testArr[4][3][2] { if retrievedArr[4][3][2] != testArr[4][3][2] {
t.Fatalf("Retrieved value does not match expected value! got: %d, expected: %d. %v", retrievedArr[4][3][2], testArr[4][3][2], err) t.Fatalf("Retrieved value does not match expected value! got: %d, expected: %d. %v", retrievedArr[4][3][2], testArr[4][3][2], err)
}`, }
`,
}, },
} }

View file

@ -23,8 +23,8 @@ environment:
install: install:
- git submodule update --init - git submodule update --init
- rmdir C:\go /s /q - rmdir C:\go /s /q
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.9.2.windows-%GETH_ARCH%.zip - appveyor DownloadFile https://storage.googleapis.com/golang/go1.10.windows-%GETH_ARCH%.zip
- 7z x go1.9.2.windows-%GETH_ARCH%.zip -y -oC:\ > NUL - 7z x go1.10.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
- go version - go version
- gcc --version - gcc --version

View file

@ -2,12 +2,7 @@
Tagged releases and develop branch commits are available as installable Debian packages Tagged releases and develop branch commits are available as installable Debian packages
for Ubuntu. Packages are built for the all Ubuntu versions which are supported by for Ubuntu. Packages are built for the all Ubuntu versions which are supported by
Canonical: Canonical.
- Trusty Tahr (14.04 LTS)
- Xenial Xerus (16.04 LTS)
- Yakkety Yak (16.10)
- Zesty Zapus (17.04)
Packages of develop branch commits have suffix -unstable and cannot be installed alongside Packages of develop branch commits have suffix -unstable and cannot be installed alongside
the stable version. Switching between release streams requires user intervention. the stable version. Switching between release streams requires user intervention.
@ -21,18 +16,18 @@ variable which Travis CI makes available to certain builds.
We want to build go-ethereum with the most recent version of Go, irrespective of the Go We want to build go-ethereum with the most recent version of Go, irrespective of the Go
version that is available in the main Ubuntu repository. In order to make this possible, version that is available in the main Ubuntu repository. In order to make this possible,
our PPA depends on the ~gophers/ubuntu/archive PPA. Our source package build-depends on our PPA depends on the ~gophers/ubuntu/archive PPA. Our source package build-depends on
golang-1.9, which is co-installable alongside the regular golang package. PPA dependencies golang-1.10, which is co-installable alongside the regular golang package. PPA dependencies
can be edited at https://launchpad.net/%7Eethereum/+archive/ubuntu/ethereum/+edit-dependencies can be edited at https://launchpad.net/%7Eethereum/+archive/ubuntu/ethereum/+edit-dependencies
## Building Packages Locally (for testing) ## Building Packages Locally (for testing)
You need to run Ubuntu to do test packaging. You need to run Ubuntu to do test packaging.
Add the gophers PPA and install Go 1.9 and Debian packaging tools: Add the gophers PPA and install Go 1.10 and Debian packaging tools:
$ sudo apt-add-repository ppa:gophers/ubuntu/archive $ sudo apt-add-repository ppa:gophers/ubuntu/archive
$ sudo apt-get update $ sudo apt-get update
$ sudo apt-get install build-essential golang-1.9 devscripts debhelper $ sudo apt-get install build-essential golang-1.10 devscripts debhelper
Create the source packages: Create the source packages:

View file

@ -2,7 +2,7 @@ Source: {{.Name}}
Section: science Section: science
Priority: extra Priority: extra
Maintainer: {{.Author}} Maintainer: {{.Author}}
Build-Depends: debhelper (>= 8.0.0), golang-1.9 Build-Depends: debhelper (>= 8.0.0), golang-1.10
Standards-Version: 3.9.5 Standards-Version: 3.9.5
Homepage: https://ethereum.org Homepage: https://ethereum.org
Vcs-Git: git://github.com/ethereum/go-ethereum.git Vcs-Git: git://github.com/ethereum/go-ethereum.git

View file

@ -5,7 +5,7 @@
#export DH_VERBOSE=1 #export DH_VERBOSE=1
override_dh_auto_build: override_dh_auto_build:
build/env.sh /usr/lib/go-1.9/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}} build/env.sh /usr/lib/go-1.10/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}}
override_dh_auto_test: override_dh_auto_test:

View file

@ -225,6 +225,13 @@ func importChain(ctx *cli.Context) error {
utils.Fatalf("Failed to read database stats: %v", err) utils.Fatalf("Failed to read database stats: %v", err)
} }
fmt.Println(stats) fmt.Println(stats)
ioStats, err := db.LDB().GetProperty("leveldb.iostats")
if err != nil {
utils.Fatalf("Failed to read database iostats: %v", err)
}
fmt.Println(ioStats)
fmt.Printf("Trie cache misses: %d\n", trie.CacheMisses()) fmt.Printf("Trie cache misses: %d\n", trie.CacheMisses())
fmt.Printf("Trie cache unloads: %d\n\n", trie.CacheUnloads()) fmt.Printf("Trie cache unloads: %d\n\n", trie.CacheUnloads())
@ -255,6 +262,12 @@ func importChain(ctx *cli.Context) error {
} }
fmt.Println(stats) fmt.Println(stats)
ioStats, err = db.LDB().GetProperty("leveldb.iostats")
if err != nil {
utils.Fatalf("Failed to read database iostats: %v", err)
}
fmt.Println(ioStats)
return nil return nil
} }

View file

@ -65,7 +65,6 @@ var (
utils.DashboardAddrFlag, utils.DashboardAddrFlag,
utils.DashboardPortFlag, utils.DashboardPortFlag,
utils.DashboardRefreshFlag, utils.DashboardRefreshFlag,
utils.DashboardAssetsFlag,
utils.EthashCacheDirFlag, utils.EthashCacheDirFlag,
utils.EthashCachesInMemoryFlag, utils.EthashCachesInMemoryFlag,
utils.EthashCachesOnDiskFlag, utils.EthashCachesOnDiskFlag,

View file

@ -37,7 +37,7 @@ ADD genesis.json /genesis.json
RUN \ RUN \
echo 'node server.js &' > wallet.sh && \ echo 'node server.js &' > wallet.sh && \
echo 'geth --cache 512 init /genesis.json' >> wallet.sh && \ echo 'geth --cache 512 init /genesis.json' >> wallet.sh && \
echo $'geth --networkid {{.NetworkID}} --port {{.NodePort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --rpc --rpcaddr=0.0.0.0 --rpccorsdomain "*"' >> wallet.sh echo $'geth --networkid {{.NetworkID}} --port {{.NodePort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --rpc --rpcaddr=0.0.0.0 --rpccorsdomain "*" --rpcvhosts "*"' >> wallet.sh
RUN \ RUN \
sed -i 's/PuppethNetworkID/{{.NetworkID}}/g' dist/js/etherwallet-master.js && \ sed -i 's/PuppethNetworkID/{{.NetworkID}}/g' dist/js/etherwallet-master.js && \

View file

@ -209,11 +209,6 @@ var (
Usage: "Dashboard metrics collection refresh rate", Usage: "Dashboard metrics collection refresh rate",
Value: dashboard.DefaultConfig.Refresh, Value: dashboard.DefaultConfig.Refresh,
} }
DashboardAssetsFlag = cli.StringFlag{
Name: "dashboard.assets",
Usage: "Developer flag to serve the dashboard from the local file system",
Value: dashboard.DefaultConfig.Assets,
}
// Ethash settings // Ethash settings
EthashCacheDirFlag = DirectoryFlag{ EthashCacheDirFlag = DirectoryFlag{
Name: "ethash.cachedir", Name: "ethash.cachedir",
@ -819,6 +814,9 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
if ctx.GlobalIsSet(MaxPeersFlag.Name) { if ctx.GlobalIsSet(MaxPeersFlag.Name) {
cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name) cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name)
if lightServer && !ctx.GlobalIsSet(LightPeersFlag.Name) {
cfg.MaxPeers += lightPeers
}
} else { } else {
if lightServer { if lightServer {
cfg.MaxPeers += lightPeers cfg.MaxPeers += lightPeers
@ -1120,7 +1118,6 @@ func SetDashboardConfig(ctx *cli.Context, cfg *dashboard.Config) {
cfg.Host = ctx.GlobalString(DashboardAddrFlag.Name) cfg.Host = ctx.GlobalString(DashboardAddrFlag.Name)
cfg.Port = ctx.GlobalInt(DashboardPortFlag.Name) cfg.Port = ctx.GlobalInt(DashboardPortFlag.Name)
cfg.Refresh = ctx.GlobalDuration(DashboardRefreshFlag.Name) cfg.Refresh = ctx.GlobalDuration(DashboardRefreshFlag.Name)
cfg.Assets = ctx.GlobalString(DashboardAssetsFlag.Name)
} }
// RegisterEthService adds an Ethereum client to the stack. // RegisterEthService adds an Ethereum client to the stack.

View file

@ -197,6 +197,8 @@ func initialize() {
if len(*argIP) == 0 { if len(*argIP) == 0 {
argIP = scanLineA("Please enter your IP and port (e.g. 127.0.0.1:30348): ") argIP = scanLineA("Please enter your IP and port (e.g. 127.0.0.1:30348): ")
} }
} else if *fileReader {
*bootstrapMode = true
} else { } else {
if len(*argEnode) == 0 { if len(*argEnode) == 0 {
argEnode = scanLineA("Please enter the peer's enode: ") argEnode = scanLineA("Please enter the peer's enode: ")
@ -205,11 +207,22 @@ func initialize() {
peers = append(peers, peer) peers = append(peers, peer)
} }
if *mailServerMode {
if len(msPassword) == 0 {
msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ")
if err != nil {
utils.Fatalf("Failed to read Mail Server password: %s", err)
}
}
}
cfg := &whisper.Config{ cfg := &whisper.Config{
MaxMessageSize: uint32(*argMaxSize), MaxMessageSize: uint32(*argMaxSize),
MinimumAcceptedPOW: *argPoW, MinimumAcceptedPOW: *argPoW,
} }
shh = whisper.New(cfg)
if *argPoW != whisper.DefaultMinimumPoW { if *argPoW != whisper.DefaultMinimumPoW {
err := shh.SetMinimumPoW(*argPoW) err := shh.SetMinimumPoW(*argPoW)
if err != nil { if err != nil {
@ -257,18 +270,8 @@ func initialize() {
} }
if *mailServerMode { if *mailServerMode {
if len(msPassword) == 0 {
msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ")
if err != nil {
utils.Fatalf("Failed to read Mail Server password: %s", err)
}
}
shh = whisper.New(cfg)
shh.RegisterServer(&mailServer) shh.RegisterServer(&mailServer)
mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW) mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW)
} else {
shh = whisper.New(cfg)
} }
server = &p2p.Server{ server = &p2p.Server{
@ -306,7 +309,11 @@ func startServer() error {
configureNode() configureNode()
} }
if !*forwarderMode { if *fileExMode {
fmt.Printf("Please type the file name to be send. To quit type: '%s'\n", quitCommand)
} else if *fileReader {
fmt.Printf("Please type the file name to be decrypted. To quit type: '%s'\n", quitCommand)
} else if !*forwarderMode {
fmt.Printf("Please type the message. To quit type: '%s'\n", quitCommand) fmt.Printf("Please type the message. To quit type: '%s'\n", quitCommand)
} }
return nil return nil
@ -566,6 +573,7 @@ func sendMsg(payload []byte) common.Hash {
if err != nil { if err != nil {
utils.Fatalf("failed to create new message: %s", err) utils.Fatalf("failed to create new message: %s", err)
} }
envelope, err := msg.Wrap(&params) envelope, err := msg.Wrap(&params)
if err != nil { if err != nil {
fmt.Printf("failed to seal message: %v \n", err) fmt.Printf("failed to seal message: %v \n", err)
@ -601,15 +609,17 @@ func messageLoop() {
m2 := af.Retrieve() m2 := af.Retrieve()
messages := append(m1, m2...) messages := append(m1, m2...)
for _, msg := range messages { for _, msg := range messages {
reportedOnce := false
if !*fileExMode && len(msg.Payload) <= 2048 {
printMessageInfo(msg)
reportedOnce = true
}
// All messages are saved upon specifying argSaveDir. // All messages are saved upon specifying argSaveDir.
// fileExMode only specifies how messages are displayed on the console after they are saved. // fileExMode only specifies how messages are displayed on the console after they are saved.
// if fileExMode == true, only the hashes are displayed, since messages might be too big. // if fileExMode == true, only the hashes are displayed, since messages might be too big.
if len(*argSaveDir) > 0 { if len(*argSaveDir) > 0 {
writeMessageToFile(*argSaveDir, msg) writeMessageToFile(*argSaveDir, msg, !reportedOnce)
}
if !*fileExMode && len(msg.Payload) <= 2048 {
printMessageInfo(msg)
} }
} }
case <-done: case <-done:
@ -634,7 +644,11 @@ func printMessageInfo(msg *whisper.ReceivedMessage) {
} }
} }
func writeMessageToFile(dir string, msg *whisper.ReceivedMessage) { func writeMessageToFile(dir string, msg *whisper.ReceivedMessage, show bool) {
if len(dir) == 0 {
return
}
timestamp := fmt.Sprintf("%d", msg.Sent) timestamp := fmt.Sprintf("%d", msg.Sent)
name := fmt.Sprintf("%x", msg.EnvelopeHash) name := fmt.Sprintf("%x", msg.EnvelopeHash)
@ -643,22 +657,24 @@ func writeMessageToFile(dir string, msg *whisper.ReceivedMessage) {
address = crypto.PubkeyToAddress(*msg.Src) address = crypto.PubkeyToAddress(*msg.Src)
} }
env := shh.GetEnvelope(msg.EnvelopeHash)
if env == nil {
fmt.Printf("\nUnexpected error: envelope not found: %x\n", msg.EnvelopeHash)
return
}
// this is a sample code; uncomment if you don't want to save your own messages. // this is a sample code; uncomment if you don't want to save your own messages.
//if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) { //if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) {
// fmt.Printf("\n%s <%x>: message from myself received, not saved: '%s'\n", timestamp, address, name) // fmt.Printf("\n%s <%x>: message from myself received, not saved: '%s'\n", timestamp, address, name)
// return // return
//} //}
if len(dir) > 0 { fullpath := filepath.Join(dir, name)
fullpath := filepath.Join(dir, name) err := ioutil.WriteFile(fullpath, env.Data, 0644)
err := ioutil.WriteFile(fullpath, msg.Raw, 0644) if err != nil {
if err != nil { fmt.Printf("\n%s {%x}: message received but not saved: %s\n", timestamp, address, err)
fmt.Printf("\n%s {%x}: message received but not saved: %s\n", timestamp, address, err) } else if show {
} else { fmt.Printf("\n%s {%x}: message received and saved as '%s' (%d bytes)\n", timestamp, address, name, len(env.Data))
fmt.Printf("\n%s {%x}: message received and saved as '%s' (%d bytes)\n", timestamp, address, name, len(msg.Raw))
}
} else {
fmt.Printf("\n%s {%x}: message received (%d bytes), but not saved: %s\n", timestamp, address, len(msg.Raw), name)
} }
} }
@ -682,18 +698,23 @@ func requestExpiredMessagesLoop() {
for { for {
timeLow = scanUint("Please enter the lower limit of the time range (unix timestamp): ") timeLow = scanUint("Please enter the lower limit of the time range (unix timestamp): ")
timeUpp = scanUint("Please enter the upper limit of the time range (unix timestamp): ") timeUpp = scanUint("Please enter the upper limit of the time range (unix timestamp): ")
t = scanLine("Please enter the topic (hexadecimal): ") t = scanLine("Enter the topic (hex). Press enter to request all messages, regardless of the topic: ")
if len(t) >= whisper.TopicLength*2 { if len(t) == whisper.TopicLength*2 {
x, err := hex.DecodeString(t) x, err := hex.DecodeString(t)
if err != nil { if err != nil {
utils.Fatalf("Failed to parse the topic: %s", err) fmt.Printf("Failed to parse the topic: %s \n", err)
continue
} }
xt = whisper.BytesToTopic(x) xt = whisper.BytesToTopic(x)
bloom = whisper.TopicToBloom(xt) bloom = whisper.TopicToBloom(xt)
obfuscateBloom(bloom) obfuscateBloom(bloom)
} else { } else if len(t) == 0 {
bloom = whisper.MakeFullNodeBloom() bloom = whisper.MakeFullNodeBloom()
} else {
fmt.Println("Error: topic is invalid, request aborted")
continue
} }
if timeUpp == 0 { if timeUpp == 0 {
timeUpp = 0xFFFFFFFF timeUpp = 0xFFFFFFFF
} }

View file

@ -65,7 +65,6 @@ type solcOutput struct {
func (s *Solidity) makeArgs() []string { func (s *Solidity) makeArgs() []string {
p := []string{ p := []string{
"--combined-json", "bin,abi,userdoc,devdoc", "--combined-json", "bin,abi,userdoc,devdoc",
"--add-std", // include standard lib contracts
"--optimize", // code optimizer switched on "--optimize", // code optimizer switched on
} }
if s.Major > 0 || s.Minor > 4 || s.Patch > 6 { if s.Major > 0 || s.Minor > 4 || s.Patch > 6 {

View file

@ -723,10 +723,13 @@ func (bc *BlockChain) Rollback(chain []common.Hash) {
} }
// SetReceiptsData computes all the non-consensus fields of the receipts // SetReceiptsData computes all the non-consensus fields of the receipts
func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts types.Receipts) { func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts types.Receipts) error {
signer := types.MakeSigner(config, block.Number()) signer := types.MakeSigner(config, block.Number())
transactions, logIndex := block.Transactions(), uint(0) transactions, logIndex := block.Transactions(), uint(0)
if len(transactions) != len(receipts) {
return errors.New("transaction and receipt count mismatch")
}
for j := 0; j < len(receipts); j++ { for j := 0; j < len(receipts); j++ {
// The transaction hash can be retrieved from the transaction itself // The transaction hash can be retrieved from the transaction itself
@ -754,6 +757,7 @@ func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts ty
logIndex++ logIndex++
} }
} }
return nil
} }
// InsertReceiptChain attempts to complete an already existing header chain with // InsertReceiptChain attempts to complete an already existing header chain with
@ -794,7 +798,9 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
continue continue
} }
// Compute all the non-consensus fields of the receipts // Compute all the non-consensus fields of the receipts
SetReceiptsData(bc.chainConfig, block, receipts) if err := SetReceiptsData(bc.chainConfig, block, receipts); err != nil {
return i, fmt.Errorf("failed to set receipts data: %v", err)
}
// Write all the data out into the database // Write all the data out into the database
if err := WriteBody(batch, block.Hash(), block.NumberU64(), block.Body()); err != nil { if err := WriteBody(batch, block.Hash(), block.NumberU64(), block.Body()); err != nil {
return i, fmt.Errorf("failed to write block body: %v", err) return i, fmt.Errorf("failed to write block body: %v", err)

View file

@ -877,15 +877,14 @@ func (pool *TxPool) removeTx(hash common.Hash) {
// Remove the transaction from the pending lists and reset the account nonce // Remove the transaction from the pending lists and reset the account nonce
if pending := pool.pending[addr]; pending != nil { if pending := pool.pending[addr]; pending != nil {
if removed, invalids := pending.Remove(tx); removed { if removed, invalids := pending.Remove(tx); removed {
// If no more transactions are left, remove the list // If no more pending transactions are left, remove the list
if pending.Empty() { if pending.Empty() {
delete(pool.pending, addr) delete(pool.pending, addr)
delete(pool.beats, addr) delete(pool.beats, addr)
} else { }
// Otherwise postpone any invalidated transactions // Postpone any invalidated transactions
for _, tx := range invalids { for _, tx := range invalids {
pool.enqueueTx(tx.Hash(), tx) pool.enqueueTx(tx.Hash(), tx)
}
} }
// Update the account nonce if needed // Update the account nonce if needed
if nonce := tx.Nonce(); pool.pendingState.GetNonce(addr) > nonce { if nonce := tx.Nonce(); pool.pendingState.GetNonce(addr) > nonce {

View file

@ -557,74 +557,112 @@ func TestTransactionDropping(t *testing.T) {
func TestTransactionPostponing(t *testing.T) { func TestTransactionPostponing(t *testing.T) {
t.Parallel() t.Parallel()
// Create a test account and fund it // Create the pool to test the postponing with
pool, key := setupTxPool() db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
defer pool.Stop() defer pool.Stop()
account, _ := deriveSender(transaction(0, 0, key)) // Create two test accounts to produce different gap profiles with
pool.currentState.AddBalance(account, big.NewInt(1000)) keys := make([]*ecdsa.PrivateKey, 2)
accs := make([]common.Address, len(keys))
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
accs[i] = crypto.PubkeyToAddress(keys[i].PublicKey)
pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(50100))
}
// Add a batch consecutive pending transactions for validation // Add a batch consecutive pending transactions for validation
txns := []*types.Transaction{} txs := []*types.Transaction{}
for i := 0; i < 100; i++ { for i, key := range keys {
var tx *types.Transaction
if i%2 == 0 { for j := 0; j < 100; j++ {
tx = transaction(uint64(i), 100, key) var tx *types.Transaction
} else { if (i+j)%2 == 0 {
tx = transaction(uint64(i), 500, key) tx = transaction(uint64(j), 25000, key)
} else {
tx = transaction(uint64(j), 50000, key)
}
txs = append(txs, tx)
}
}
for i, err := range pool.AddRemotes(txs) {
if err != nil {
t.Fatalf("tx %d: failed to add transactions: %v", i, err)
} }
pool.promoteTx(account, tx.Hash(), tx)
txns = append(txns, tx)
} }
// Check that pre and post validations leave the pool as is // Check that pre and post validations leave the pool as is
if pool.pending[account].Len() != len(txns) { if pending := pool.pending[accs[0]].Len() + pool.pending[accs[1]].Len(); pending != len(txs) {
t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), len(txns)) t.Errorf("pending transaction mismatch: have %d, want %d", pending, len(txs))
} }
if len(pool.queue) != 0 { if len(pool.queue) != 0 {
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 0) t.Errorf("queued accounts mismatch: have %d, want %d", len(pool.queue), 0)
} }
if len(pool.all) != len(txns) { if len(pool.all) != len(txs) {
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txns)) t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txs))
} }
pool.lockedReset(nil, nil) pool.lockedReset(nil, nil)
if pool.pending[account].Len() != len(txns) { if pending := pool.pending[accs[0]].Len() + pool.pending[accs[1]].Len(); pending != len(txs) {
t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), len(txns)) t.Errorf("pending transaction mismatch: have %d, want %d", pending, len(txs))
} }
if len(pool.queue) != 0 { if len(pool.queue) != 0 {
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 0) t.Errorf("queued accounts mismatch: have %d, want %d", len(pool.queue), 0)
} }
if len(pool.all) != len(txns) { if len(pool.all) != len(txs) {
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txns)) t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txs))
} }
// Reduce the balance of the account, and check that transactions are reorganised // Reduce the balance of the account, and check that transactions are reorganised
pool.currentState.AddBalance(account, big.NewInt(-750)) for _, addr := range accs {
pool.currentState.AddBalance(addr, big.NewInt(-1))
}
pool.lockedReset(nil, nil) pool.lockedReset(nil, nil)
if _, ok := pool.pending[account].txs.items[txns[0].Nonce()]; !ok { // The first account's first transaction remains valid, check that subsequent
t.Errorf("tx %d: valid and funded transaction missing from pending pool: %v", 0, txns[0]) // ones are either filtered out, or queued up for later.
if _, ok := pool.pending[accs[0]].txs.items[txs[0].Nonce()]; !ok {
t.Errorf("tx %d: valid and funded transaction missing from pending pool: %v", 0, txs[0])
} }
if _, ok := pool.queue[account].txs.items[txns[0].Nonce()]; ok { if _, ok := pool.queue[accs[0]].txs.items[txs[0].Nonce()]; ok {
t.Errorf("tx %d: valid and funded transaction present in future queue: %v", 0, txns[0]) t.Errorf("tx %d: valid and funded transaction present in future queue: %v", 0, txs[0])
} }
for i, tx := range txns[1:] { for i, tx := range txs[1:100] {
if i%2 == 1 { if i%2 == 1 {
if _, ok := pool.pending[account].txs.items[tx.Nonce()]; ok { if _, ok := pool.pending[accs[0]].txs.items[tx.Nonce()]; ok {
t.Errorf("tx %d: valid but future transaction present in pending pool: %v", i+1, tx) t.Errorf("tx %d: valid but future transaction present in pending pool: %v", i+1, tx)
} }
if _, ok := pool.queue[account].txs.items[tx.Nonce()]; !ok { if _, ok := pool.queue[accs[0]].txs.items[tx.Nonce()]; !ok {
t.Errorf("tx %d: valid but future transaction missing from future queue: %v", i+1, tx) t.Errorf("tx %d: valid but future transaction missing from future queue: %v", i+1, tx)
} }
} else { } else {
if _, ok := pool.pending[account].txs.items[tx.Nonce()]; ok { if _, ok := pool.pending[accs[0]].txs.items[tx.Nonce()]; ok {
t.Errorf("tx %d: out-of-fund transaction present in pending pool: %v", i+1, tx) t.Errorf("tx %d: out-of-fund transaction present in pending pool: %v", i+1, tx)
} }
if _, ok := pool.queue[account].txs.items[tx.Nonce()]; ok { if _, ok := pool.queue[accs[0]].txs.items[tx.Nonce()]; ok {
t.Errorf("tx %d: out-of-fund transaction present in future queue: %v", i+1, tx) t.Errorf("tx %d: out-of-fund transaction present in future queue: %v", i+1, tx)
} }
} }
} }
if len(pool.all) != len(txns)/2 { // The second account's first transaction got invalid, check that all transactions
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txns)/2) // are either filtered out, or queued up for later.
if pool.pending[accs[1]] != nil {
t.Errorf("invalidated account still has pending transactions")
}
for i, tx := range txs[100:] {
if i%2 == 1 {
if _, ok := pool.queue[accs[1]].txs.items[tx.Nonce()]; !ok {
t.Errorf("tx %d: valid but future transaction missing from future queue: %v", 100+i, tx)
}
} else {
if _, ok := pool.queue[accs[1]].txs.items[tx.Nonce()]; ok {
t.Errorf("tx %d: out-of-fund transaction present in future queue: %v", 100+i, tx)
}
}
}
if len(pool.all) != len(txs)/2 {
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txs)/2)
} }
} }
@ -949,11 +987,11 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
account2, _ := deriveSender(transaction(0, 0, key2)) account2, _ := deriveSender(transaction(0, 0, key2))
pool2.currentState.AddBalance(account2, big.NewInt(1000000)) pool2.currentState.AddBalance(account2, big.NewInt(1000000))
txns := []*types.Transaction{} txs := []*types.Transaction{}
for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ { for i := uint64(0); i < testTxPoolConfig.AccountQueue+5; i++ {
txns = append(txns, transaction(origin+i, 100000, key2)) txs = append(txs, transaction(origin+i, 100000, key2))
} }
pool2.AddRemotes(txns) pool2.AddRemotes(txs)
// Ensure the batch optimization honors the same pool mechanics // Ensure the batch optimization honors the same pool mechanics
if len(pool1.pending) != len(pool2.pending) { if len(pool1.pending) != len(pool2.pending) {
@ -1124,7 +1162,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
defer sub.Unsubscribe() defer sub.Unsubscribe()
// Create a number of test accounts and fund them // Create a number of test accounts and fund them
keys := make([]*ecdsa.PrivateKey, 3) keys := make([]*ecdsa.PrivateKey, 4)
for i := 0; i < len(keys); i++ { for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey() keys[i], _ = crypto.GenerateKey()
pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000)) pool.currentState.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
@ -1136,24 +1174,28 @@ func TestTransactionPoolRepricing(t *testing.T) {
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(1), keys[0])) txs = append(txs, pricedTransaction(1, 100000, big.NewInt(1), keys[0]))
txs = append(txs, pricedTransaction(2, 100000, big.NewInt(2), keys[0])) txs = append(txs, pricedTransaction(2, 100000, big.NewInt(2), keys[0]))
txs = append(txs, pricedTransaction(0, 100000, big.NewInt(1), keys[1]))
txs = append(txs, pricedTransaction(1, 100000, big.NewInt(2), keys[1])) txs = append(txs, pricedTransaction(1, 100000, big.NewInt(2), keys[1]))
txs = append(txs, pricedTransaction(2, 100000, big.NewInt(1), keys[1])) txs = append(txs, pricedTransaction(2, 100000, big.NewInt(2), keys[1]))
txs = append(txs, pricedTransaction(3, 100000, big.NewInt(2), keys[1]))
ltx := pricedTransaction(0, 100000, big.NewInt(1), keys[2]) txs = append(txs, pricedTransaction(1, 100000, big.NewInt(2), keys[2]))
txs = append(txs, pricedTransaction(2, 100000, big.NewInt(1), keys[2]))
txs = append(txs, pricedTransaction(3, 100000, big.NewInt(2), keys[2]))
ltx := pricedTransaction(0, 100000, big.NewInt(1), keys[3])
// Import the batch and that both pending and queued transactions match up // Import the batch and that both pending and queued transactions match up
pool.AddRemotes(txs) pool.AddRemotes(txs)
pool.AddLocal(ltx) pool.AddLocal(ltx)
pending, queued := pool.Stats() pending, queued := pool.Stats()
if pending != 4 { if pending != 7 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 4) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 7)
} }
if queued != 3 { if queued != 3 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 3) t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 3)
} }
if err := validateEvents(events, 4); err != nil { if err := validateEvents(events, 7); err != nil {
t.Fatalf("original event firing failed: %v", err) t.Fatalf("original event firing failed: %v", err)
} }
if err := validateTxPoolInternals(pool); err != nil { if err := validateTxPoolInternals(pool); err != nil {
@ -1166,8 +1208,8 @@ func TestTransactionPoolRepricing(t *testing.T) {
if pending != 2 { if pending != 2 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2) t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
} }
if queued != 3 { if queued != 5 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 3) t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 5)
} }
if err := validateEvents(events, 0); err != nil { if err := validateEvents(events, 0); err != nil {
t.Fatalf("reprice event firing failed: %v", err) t.Fatalf("reprice event firing failed: %v", err)
@ -1179,7 +1221,10 @@ func TestTransactionPoolRepricing(t *testing.T) {
if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(1), keys[0])); err != ErrUnderpriced { if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(1), keys[0])); err != ErrUnderpriced {
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced) t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
} }
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(1), keys[1])); err != ErrUnderpriced { if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(1), keys[1])); err != ErrUnderpriced {
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
}
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(1), keys[2])); err != ErrUnderpriced {
t.Fatalf("adding underpriced queued transaction error mismatch: have %v, want %v", err, ErrUnderpriced) t.Fatalf("adding underpriced queued transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
} }
if err := validateEvents(events, 0); err != nil { if err := validateEvents(events, 0); err != nil {
@ -1189,7 +1234,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
t.Fatalf("pool internal state corrupted: %v", err) t.Fatalf("pool internal state corrupted: %v", err)
} }
// However we can add local underpriced transactions // However we can add local underpriced transactions
tx := pricedTransaction(1, 100000, big.NewInt(1), keys[2]) tx := pricedTransaction(1, 100000, big.NewInt(1), keys[3])
if err := pool.AddLocal(tx); err != nil { if err := pool.AddLocal(tx); err != nil {
t.Fatalf("failed to add underpriced local transaction: %v", err) t.Fatalf("failed to add underpriced local transaction: %v", err)
} }
@ -1202,6 +1247,22 @@ func TestTransactionPoolRepricing(t *testing.T) {
if err := validateTxPoolInternals(pool); err != nil { if err := validateTxPoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err) t.Fatalf("pool internal state corrupted: %v", err)
} }
// And we can fill gaps with properly priced transactions
if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(2), keys[0])); err != nil {
t.Fatalf("failed to add pending transaction: %v", err)
}
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(2), keys[1])); err != nil {
t.Fatalf("failed to add pending transaction: %v", err)
}
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(2), keys[2])); err != nil {
t.Fatalf("failed to add queued transaction: %v", err)
}
if err := validateEvents(events, 5); err != nil {
t.Fatalf("post-reprice event firing failed: %v", err)
}
if err := validateTxPoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
} }
// Tests that setting the transaction pool gas price to a higher value does not // Tests that setting the transaction pool gas price to a higher value does not

View file

@ -30,6 +30,8 @@ import (
var ( var (
bigZero = new(big.Int) bigZero = new(big.Int)
tt255 = math.BigPow(2, 255)
tt256 = math.BigPow(2, 256)
errWriteProtection = errors.New("evm: write protection") errWriteProtection = errors.New("evm: write protection")
errReturnDataOutOfBounds = errors.New("evm: return data out of bounds") errReturnDataOutOfBounds = errors.New("evm: return data out of bounds")
errExecutionReverted = errors.New("evm: execution reverted") errExecutionReverted = errors.New("evm: execution reverted")
@ -191,50 +193,71 @@ func opGt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack
} }
func opSlt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opSlt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := math.S256(stack.pop()), math.S256(stack.pop()) x, y := stack.pop(), stack.peek()
if x.Cmp(math.S256(y)) < 0 {
stack.push(evm.interpreter.intPool.get().SetUint64(1))
} else {
stack.push(new(big.Int))
}
evm.interpreter.intPool.put(x, y) xSign := x.Cmp(tt255)
ySign := y.Cmp(tt255)
switch {
case xSign >= 0 && ySign < 0:
y.SetUint64(1)
case xSign < 0 && ySign >= 0:
y.SetUint64(0)
default:
if x.Cmp(y) < 0 {
y.SetUint64(1)
} else {
y.SetUint64(0)
}
}
evm.interpreter.intPool.put(x)
return nil, nil return nil, nil
} }
func opSgt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opSgt(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := math.S256(stack.pop()), math.S256(stack.pop()) x, y := stack.pop(), stack.peek()
if x.Cmp(y) > 0 {
stack.push(evm.interpreter.intPool.get().SetUint64(1))
} else {
stack.push(new(big.Int))
}
evm.interpreter.intPool.put(x, y) xSign := x.Cmp(tt255)
ySign := y.Cmp(tt255)
switch {
case xSign >= 0 && ySign < 0:
y.SetUint64(0)
case xSign < 0 && ySign >= 0:
y.SetUint64(1)
default:
if x.Cmp(y) > 0 {
y.SetUint64(1)
} else {
y.SetUint64(0)
}
}
evm.interpreter.intPool.put(x)
return nil, nil return nil, nil
} }
func opEq(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opEq(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x, y := stack.pop(), stack.pop() x, y := stack.pop(), stack.peek()
if x.Cmp(y) == 0 { if x.Cmp(y) == 0 {
stack.push(evm.interpreter.intPool.get().SetUint64(1)) y.SetUint64(1)
} else { } else {
stack.push(new(big.Int)) y.SetUint64(0)
} }
evm.interpreter.intPool.put(x)
evm.interpreter.intPool.put(x, y)
return nil, nil return nil, nil
} }
func opIszero(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opIszero(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
x := stack.pop() x := stack.peek()
if x.Sign() > 0 { if x.Sign() > 0 {
stack.push(new(big.Int)) x.SetUint64(0)
} else { } else {
stack.push(evm.interpreter.intPool.get().SetUint64(1)) x.SetUint64(1)
} }
evm.interpreter.intPool.put(x)
return nil, nil return nil, nil
} }

View file

@ -161,6 +161,7 @@ func TestSAR(t *testing.T) {
func TestSGT(t *testing.T) { func TestSGT(t *testing.T) {
tests := []twoOperandTest{ tests := []twoOperandTest{
{"0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, {"0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"},
{"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"},
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"},
@ -171,6 +172,8 @@ func TestSGT(t *testing.T) {
{"8000000000000000000000000000000000000000000000000000000000000001", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, {"8000000000000000000000000000000000000000000000000000000000000001", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"},
{"8000000000000000000000000000000000000000000000000000000000000001", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001"}, {"8000000000000000000000000000000000000000000000000000000000000001", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000001"},
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"},
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd", "0000000000000000000000000000000000000000000000000000000000000001"},
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb", "0000000000000000000000000000000000000000000000000000000000000000"},
} }
testTwoOperandOp(t, tests, opSgt) testTwoOperandOp(t, tests, opSgt)
} }
@ -187,6 +190,8 @@ func TestSLT(t *testing.T) {
{"8000000000000000000000000000000000000000000000000000000000000001", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"}, {"8000000000000000000000000000000000000000000000000000000000000001", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000"},
{"8000000000000000000000000000000000000000000000000000000000000001", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"}, {"8000000000000000000000000000000000000000000000000000000000000001", "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "0000000000000000000000000000000000000000000000000000000000000000"},
{"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001"}, {"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "8000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000001"},
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd", "0000000000000000000000000000000000000000000000000000000000000000"},
{"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd", "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb", "0000000000000000000000000000000000000000000000000000000000000001"},
} }
testTwoOperandOp(t, tests, opSlt) testTwoOperandOp(t, tests, opSlt)
} }
@ -349,7 +354,11 @@ func BenchmarkOpEq(b *testing.B) {
opBenchmark(b, opEq, x, y) opBenchmark(b, opEq, x, y)
} }
func BenchmarkOpEq2(b *testing.B) {
x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff"
y := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201fffffffe"
opBenchmark(b, opEq, x, y)
}
func BenchmarkOpAnd(b *testing.B) { func BenchmarkOpAnd(b *testing.B) {
x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" x := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff" y := "ABCDEF090807060504030201ffffffffffffffffffffffffffffffffffffffff"
@ -412,3 +421,7 @@ func BenchmarkOpSAR(b *testing.B) {
opBenchmark(b, opSAR, x, y) opBenchmark(b, opSAR, x, y)
} }
func BenchmarkOpIsZero(b *testing.B) {
x := "FBCDEF090807060504030201ffffffffFBCDEF090807060504030201ffffffff"
opBenchmark(b, opIszero, x)
}

View file

@ -12,28 +12,27 @@ The client's UI uses [React][React] with JSX syntax, which is validated by the [
As the dashboard depends on certain NPM packages (which are not included in the `go-ethereum` repo), these need to be installed first: As the dashboard depends on certain NPM packages (which are not included in the `go-ethereum` repo), these need to be installed first:
``` ```
$ (cd dashboard/assets && npm install) $ (cd dashboard/assets && yarn install && yarn flow)
$ (cd dashboard/assets && ./node_modules/.bin/flow-typed install)
``` ```
Normally the dashboard assets are bundled into Geth via `go-bindata` to avoid external dependencies. Rebuilding Geth after each UI modification however is not feasible from a developer perspective. Instead, we can run `webpack` in watch mode to automatically rebundle the UI, and ask `geth` to use external assets to not rely on compiled resources: Normally the dashboard assets are bundled into Geth via `go-bindata` to avoid external dependencies. Rebuilding Geth after each UI modification however is not feasible from a developer perspective. Instead, we can run `yarn dev` to watch for file system changes and refresh the browser automatically.
``` ```
$ (cd dashboard/assets && ./node_modules/.bin/webpack --watch) $ geth --dashboard --vmodule=dashboard=5
$ geth --dashboard --dashboard.assets=dashboard/assets --vmodule=dashboard=5 $ (cd dashboard/assets && yarn dev)
``` ```
To bundle up the final UI into Geth, run `go generate`: To bundle up the final UI into Geth, run `go generate`:
``` ```
$ go generate ./dashboard $ (cd dashboard && go generate)
``` ```
### Static type checking ### Static type checking
Since JavaScript doesn't provide type safety, [Flow][Flow] is used to check types. These are only useful during development, so at the end of the process Babel will strip them. Since JavaScript doesn't provide type safety, [Flow][Flow] is used to check types. These are only useful during development, so at the end of the process Babel will strip them.
To take advantage of static type checking, your IDE needs to be prepared for it. In case of [Atom][Atom] a configuration guide can be found [here][Atom config]: Install the [Nuclide][Nuclide] package for Flow support, making sure it installs all of its support packages by enabling `Install Recommended Packages on Startup`, and set the path of the `flow-bin` which were installed previously by `npm`. To take advantage of static type checking, your IDE needs to be prepared for it. In case of [Atom][Atom] a configuration guide can be found [here][Atom config]: Install the [Nuclide][Nuclide] package for Flow support, making sure it installs all of its support packages by enabling `Install Recommended Packages on Startup`, and set the path of the `flow-bin` which were installed previously by `yarn`.
For more IDE support install the `linter-eslint` package too, which finds the `.eslintrc` file, and provides real-time linting. Atom warns, that these two packages are incompatible, but they seem to work well together. For third-party library errors and auto-completion [flow-typed][flow-typed] is used. For more IDE support install the `linter-eslint` package too, which finds the `.eslintrc` file, and provides real-time linting. Atom warns, that these two packages are incompatible, but they seem to work well together. For third-party library errors and auto-completion [flow-typed][flow-typed] is used.
@ -41,7 +40,7 @@ For more IDE support install the `linter-eslint` package too, which finds the `.
[Webpack][Webpack] offers handy tools for visualizing the bundle's dependency tree and space usage. [Webpack][Webpack] offers handy tools for visualizing the bundle's dependency tree and space usage.
* Generate the bundle's profile running `webpack --profile --json > stats.json` * Generate the bundle's profile running `yarn stats`
* For the _dependency tree_ go to [Webpack Analyze][WA], and import `stats.json` * For the _dependency tree_ go to [Webpack Analyze][WA], and import `stats.json`
* For the _space usage_ go to [Webpack Visualizer][WV], and import `stats.json` * For the _space usage_ go to [Webpack Visualizer][WV], and import `stats.json`

File diff suppressed because one or more lines are too long

View file

@ -38,15 +38,15 @@ export const percentPlotter = <T>(text: string, mapper: (T => T) = multiplier(1)
}; };
// unit contains the units for the bytePlotter. // unit contains the units for the bytePlotter.
const unit = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']; const unit = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'];
// simplifyBytes returns the simplified version of the given value followed by the unit. // simplifyBytes returns the simplified version of the given value followed by the unit.
const simplifyBytes = (x: number) => { const simplifyBytes = (x: number) => {
let i = 0; let i = 0;
for (; x > 1024 && i < 5; i++) { for (; x > 1024 && i < 8; i++) {
x /= 1024; x /= 1024;
} }
return x.toFixed(2).toString().concat(' ', unit[i]); return x.toFixed(2).toString().concat(' ', unit[i], 'B');
}; };
// bytePlotter renders a tooltip, which displays the payload as a byte value. // bytePlotter renders a tooltip, which displays the payload as a byte value.

View file

@ -81,7 +81,11 @@ const defaultContent: Content = {
version: null, version: null,
commit: null, commit: null,
}, },
home: { home: {},
chain: {},
txpool: {},
network: {},
system: {
activeMemory: [], activeMemory: [],
virtualMemory: [], virtualMemory: [],
networkIngress: [], networkIngress: [],
@ -91,10 +95,6 @@ const defaultContent: Content = {
diskRead: [], diskRead: [],
diskWrite: [], diskWrite: [],
}, },
chain: {},
txpool: {},
network: {},
system: {},
logs: { logs: {
log: [], log: [],
}, },
@ -108,7 +108,11 @@ const updaters = {
version: replacer, version: replacer,
commit: replacer, commit: replacer,
}, },
home: { home: null,
chain: null,
txpool: null,
network: null,
system: {
activeMemory: appender(200), activeMemory: appender(200),
virtualMemory: appender(200), virtualMemory: appender(200),
networkIngress: appender(200), networkIngress: appender(200),
@ -118,11 +122,7 @@ const updaters = {
diskRead: appender(200), diskRead: appender(200),
diskWrite: appender(200), diskWrite: appender(200),
}, },
chain: null, logs: {
txpool: null,
network: null,
system: null,
logs: {
log: appender(200), log: appender(200),
}, },
}; };
@ -136,7 +136,7 @@ const styles = {
height: '100%', height: '100%',
zIndex: 1, zIndex: 1,
overflow: 'hidden', overflow: 'hidden',
} },
}; };
// themeStyles returns the styles generated from the theme for the component. // themeStyles returns the styles generated from the theme for the component.
@ -178,7 +178,8 @@ class Dashboard extends Component<Props, State> {
// reconnect establishes a websocket connection with the server, listens for incoming messages // reconnect establishes a websocket connection with the server, listens for incoming messages
// and tries to reconnect on connection loss. // and tries to reconnect on connection loss.
reconnect = () => { reconnect = () => {
const server = new WebSocket(`${((window.location.protocol === 'https:') ? 'wss://' : 'ws://') + window.location.host}/api`); // PROD is defined by webpack.
const server = new WebSocket(`${((window.location.protocol === 'https:') ? 'wss://' : 'ws://')}${PROD ? window.location.host : 'localhost:8080'}/api`);
server.onopen = () => { server.onopen = () => {
this.setState({content: defaultContent, shouldUpdate: {}}); this.setState({content: defaultContent, shouldUpdate: {}});
}; };
@ -217,7 +218,6 @@ class Dashboard extends Component<Props, State> {
return ( return (
<div className={this.props.classes.dashboard} style={styles.dashboard}> <div className={this.props.classes.dashboard} style={styles.dashboard}>
<Header <Header
opened={this.state.sideBar}
switchSideBar={this.switchSideBar} switchSideBar={this.switchSideBar}
/> />
<Body <Body

View file

@ -26,7 +26,17 @@ import {ResponsiveContainer, AreaChart, Area, Tooltip} from 'recharts';
import ChartRow from './ChartRow'; import ChartRow from './ChartRow';
import CustomTooltip, {bytePlotter, bytePerSecPlotter, percentPlotter, multiplier} from './CustomTooltip'; import CustomTooltip, {bytePlotter, bytePerSecPlotter, percentPlotter, multiplier} from './CustomTooltip';
import {styles as commonStyles} from '../common'; import {styles as commonStyles} from '../common';
import type {Content} from '../types/content'; import type {General, System} from '../types/content';
const FOOTER_SYNC_ID = 'footerSyncId';
const CPU = 'cpu';
const MEMORY = 'memory';
const DISK = 'disk';
const TRAFFIC = 'traffic';
const TOP = 'Top';
const BOTTOM = 'Bottom';
// styles contains the constant styles of the component. // styles contains the constant styles of the component.
const styles = { const styles = {
@ -40,17 +50,16 @@ const styles = {
padding: 0, padding: 0,
}, },
doubleChartWrapper: { doubleChartWrapper: {
height: '100%', height: '100%',
width: '99%', width: '99%',
paddingTop: 5,
}, },
}; };
// themeStyles returns the styles generated from the theme for the component. // themeStyles returns the styles generated from the theme for the component.
const themeStyles: Object = (theme: Object) => ({ const themeStyles: Object = (theme: Object) => ({
footer: { footer: {
backgroundColor: theme.palette.background.appBar, backgroundColor: theme.palette.grey[900],
color: theme.palette.getContrastText(theme.palette.background.appBar), color: theme.palette.getContrastText(theme.palette.grey[900]),
zIndex: theme.zIndex.appBar, zIndex: theme.zIndex.appBar,
height: theme.spacing.unit * 10, height: theme.spacing.unit * 10,
}, },
@ -59,111 +68,108 @@ const themeStyles: Object = (theme: Object) => ({
export type Props = { export type Props = {
classes: Object, // injected by withStyles() classes: Object, // injected by withStyles()
theme: Object, theme: Object,
content: Content, general: General,
system: System,
shouldUpdate: Object, shouldUpdate: Object,
}; };
// Footer renders the footer of the dashboard. // Footer renders the footer of the dashboard.
class Footer extends Component<Props> { class Footer extends Component<Props> {
shouldComponentUpdate(nextProps) { shouldComponentUpdate(nextProps) {
return typeof nextProps.shouldUpdate.home !== 'undefined'; return typeof nextProps.shouldUpdate.general !== 'undefined' || typeof nextProps.shouldUpdate.system !== 'undefined';
} }
// info renders a label with the given values. // halfHeightChart renders an area chart with half of the height of its parent.
info = (about: string, value: ?string) => (value ? ( halfHeightChart = (chartProps, tooltip, areaProps) => (
<Typography type='caption' color='inherit'> <ResponsiveContainer width='100%' height='50%'>
<span style={commonStyles.light}>{about}</span> {value} <AreaChart {...chartProps} >
</Typography> {!tooltip || (<Tooltip cursor={false} content={<CustomTooltip tooltip={tooltip} />} />)}
) : null); <Area isAnimationActive={false} type='monotone' {...areaProps} />
</AreaChart>
</ResponsiveContainer>
);
// doubleChart renders a pair of charts separated by the baseline. // doubleChart renders a pair of charts separated by the baseline.
doubleChart = (syncId, topChart, bottomChart) => { doubleChart = (syncId, chartKey, topChart, bottomChart) => {
const topKey = 'topKey'; if (!Array.isArray(topChart.data) || !Array.isArray(bottomChart.data)) {
const bottomKey = 'bottomKey'; return null;
const topDefault = topChart.default ? topChart.default : 0; }
const bottomDefault = bottomChart.default ? bottomChart.default : 0; const topDefault = topChart.default || 0;
const topTooltip = topChart.tooltip ? ( const bottomDefault = bottomChart.default || 0;
<Tooltip cursor={false} content={<CustomTooltip tooltip={topChart.tooltip} />} /> const topKey = `${chartKey}${TOP}`;
) : null; const bottomKey = `${chartKey}${BOTTOM}`;
const bottomTooltip = bottomChart.tooltip ? (
<Tooltip cursor={false} content={<CustomTooltip tooltip={bottomChart.tooltip} />} />
) : null;
const topColor = '#8884d8'; const topColor = '#8884d8';
const bottomColor = '#82ca9d'; const bottomColor = '#82ca9d';
// Put the samples of the two charts into the same array in order to avoid problems
// at the synchronized area charts. If one of the two arrays doesn't have value at
// a given position, give it a 0 default value.
let data = [...topChart.data.map(({value}) => {
const d = {};
d[topKey] = value || topDefault;
return d;
})];
for (let i = 0; i < data.length && i < bottomChart.data.length; i++) {
// The value needs to be negative in order to plot it upside down.
const d = bottomChart.data[i];
data[i][bottomKey] = d && d.value ? -d.value : bottomDefault;
}
data = [...data, ...bottomChart.data.slice(data.length).map(({value}) => {
const d = {};
d[topKey] = topDefault;
d[bottomKey] = -value || bottomDefault;
return d;
})];
return ( return (
<div style={styles.doubleChartWrapper}> <div style={styles.doubleChartWrapper}>
<ResponsiveContainer width='100%' height='50%'> {this.halfHeightChart(
<AreaChart data={data} syncId={syncId} > {
{topTooltip} syncId,
<Area type='monotone' dataKey={topKey} stroke={topColor} fill={topColor} /> data: topChart.data.map(({value}) => ({[topKey]: value || topDefault})),
</AreaChart> margin: {top: 5, right: 5, bottom: 0, left: 5},
</ResponsiveContainer> },
<div style={{marginTop: -10, width: '100%', height: '50%'}}> topChart.tooltip,
<ResponsiveContainer width='100%' height='100%'> {dataKey: topKey, stroke: topColor, fill: topColor},
<AreaChart data={data} syncId={syncId} > )}
{bottomTooltip} {this.halfHeightChart(
<Area type='monotone' dataKey={bottomKey} stroke={bottomColor} fill={bottomColor} /> {
</AreaChart> syncId,
</ResponsiveContainer> data: bottomChart.data.map(({value}) => ({[bottomKey]: -value || -bottomDefault})),
</div> margin: {top: 0, right: 5, bottom: 5, left: 5},
},
bottomChart.tooltip,
{dataKey: bottomKey, stroke: bottomColor, fill: bottomColor},
)}
</div> </div>
); );
} };
render() { render() {
const {content} = this.props; const {general, system} = this.props;
const {general, home} = content;
return ( return (
<Grid container className={this.props.classes.footer} direction='row' alignItems='center' style={styles.footer}> <Grid container className={this.props.classes.footer} direction='row' alignItems='center' style={styles.footer}>
<Grid item xs style={styles.chartRowWrapper}> <Grid item xs style={styles.chartRowWrapper}>
<ChartRow> <ChartRow>
{this.doubleChart( {this.doubleChart(
'all', FOOTER_SYNC_ID,
{data: home.processCPU, tooltip: percentPlotter('Process')}, CPU,
{data: home.systemCPU, tooltip: percentPlotter('System', multiplier(-1))}, {data: system.processCPU, tooltip: percentPlotter('Process load')},
{data: system.systemCPU, tooltip: percentPlotter('System load', multiplier(-1))},
)} )}
{this.doubleChart( {this.doubleChart(
'all', FOOTER_SYNC_ID,
{data: home.activeMemory, tooltip: bytePlotter('Active')}, MEMORY,
{data: home.virtualMemory, tooltip: bytePlotter('Virtual', multiplier(-1))}, {data: system.activeMemory, tooltip: bytePlotter('Active memory')},
{data: system.virtualMemory, tooltip: bytePlotter('Virtual memory', multiplier(-1))},
)} )}
{this.doubleChart( {this.doubleChart(
'all', FOOTER_SYNC_ID,
{data: home.diskRead, tooltip: bytePerSecPlotter('Disk Read')}, DISK,
{data: home.diskWrite, tooltip: bytePerSecPlotter('Disk Write', multiplier(-1))}, {data: system.diskRead, tooltip: bytePerSecPlotter('Disk read')},
{data: system.diskWrite, tooltip: bytePerSecPlotter('Disk write', multiplier(-1))},
)} )}
{this.doubleChart( {this.doubleChart(
'all', FOOTER_SYNC_ID,
{data: home.networkIngress, tooltip: bytePerSecPlotter('Download')}, TRAFFIC,
{data: home.networkEgress, tooltip: bytePerSecPlotter('Upload', multiplier(-1))}, {data: system.networkIngress, tooltip: bytePerSecPlotter('Download')},
{data: system.networkEgress, tooltip: bytePerSecPlotter('Upload', multiplier(-1))},
)} )}
</ChartRow> </ChartRow>
</Grid> </Grid>
<Grid item > <Grid item >
{this.info('Geth', general.version)} <Typography type='caption' color='inherit'>
{this.info('Commit', general.commit ? general.commit.substring(0, 7) : null)} <span style={commonStyles.light}>Geth</span> {general.version}
</Typography>
{general.commit && (
<Typography type='caption' color='inherit'>
<span style={commonStyles.light}>{'Commit '}</span>
<a href={`https://github.com/ethereum/go-ethereum/commit/${general.commit}`} target='_blank' style={{color: 'inherit', textDecoration: 'none'}} >
{general.commit.substring(0, 8)}
</a>
</Typography>
)}
</Grid> </Grid>
</Grid> </Grid>
); );

View file

@ -21,30 +21,16 @@ import React, {Component} from 'react';
import withStyles from 'material-ui/styles/withStyles'; import withStyles from 'material-ui/styles/withStyles';
import AppBar from 'material-ui/AppBar'; import AppBar from 'material-ui/AppBar';
import Toolbar from 'material-ui/Toolbar'; import Toolbar from 'material-ui/Toolbar';
import Transition from 'react-transition-group/Transition';
import IconButton from 'material-ui/IconButton'; import IconButton from 'material-ui/IconButton';
import Icon from 'material-ui/Icon';
import MenuIcon from 'material-ui-icons/Menu';
import Typography from 'material-ui/Typography'; import Typography from 'material-ui/Typography';
import ChevronLeftIcon from 'material-ui-icons/ChevronLeft';
import {DURATION} from '../common';
// styles contains the constant styles of the component.
const styles = {
arrow: {
default: {
transition: `transform ${DURATION}ms`,
},
transition: {
entered: {transform: 'rotate(180deg)'},
},
},
};
// themeStyles returns the styles generated from the theme for the component. // themeStyles returns the styles generated from the theme for the component.
const themeStyles = (theme: Object) => ({ const themeStyles = (theme: Object) => ({
header: { header: {
backgroundColor: theme.palette.background.appBar, backgroundColor: theme.palette.grey[900],
color: theme.palette.getContrastText(theme.palette.background.appBar), color: theme.palette.getContrastText(theme.palette.grey[900]),
zIndex: theme.zIndex.appBar, zIndex: theme.zIndex.appBar,
}, },
toolbar: { toolbar: {
@ -53,42 +39,28 @@ const themeStyles = (theme: Object) => ({
}, },
title: { title: {
paddingLeft: theme.spacing.unit, paddingLeft: theme.spacing.unit,
fontSize: 3 * theme.spacing.unit,
}, },
}); });
export type Props = { export type Props = {
classes: Object, // injected by withStyles() classes: Object, // injected by withStyles()
opened: boolean,
switchSideBar: () => void, switchSideBar: () => void,
}; };
// Header renders the header of the dashboard. // Header renders the header of the dashboard.
class Header extends Component<Props> { class Header extends Component<Props> {
shouldComponentUpdate(nextProps) {
return nextProps.opened !== this.props.opened;
}
// arrow renders a button, which changes the sidebar's state.
arrow = (transitionState: string) => (
<IconButton onClick={this.props.switchSideBar}>
<ChevronLeftIcon
style={{
...styles.arrow.default,
...styles.arrow.transition[transitionState],
}}
/>
</IconButton>
);
render() { render() {
const {classes, opened} = this.props; const {classes} = this.props;
return ( return (
<AppBar position='static' className={classes.header}> <AppBar position='static' className={classes.header}>
<Toolbar className={classes.toolbar}> <Toolbar className={classes.toolbar}>
<Transition mountOnEnter in={opened} timeout={{enter: DURATION}}> <IconButton onClick={this.props.switchSideBar}>
{this.arrow} <Icon>
</Transition> <MenuIcon />
</Icon>
</IconButton>
<Typography type='title' color='inherit' noWrap className={classes.title}> <Typography type='title' color='inherit' noWrap className={classes.title}>
Go Ethereum Dashboard Go Ethereum Dashboard
</Typography> </Typography>

View file

@ -76,7 +76,8 @@ class Main extends Component<Props> {
<div style={styles.wrapper}> <div style={styles.wrapper}>
<div className={classes.content} style={styles.content}>{children}</div> <div className={classes.content} style={styles.content}>{children}</div>
<Footer <Footer
content={content} general={content.general}
system={content.system}
shouldUpdate={shouldUpdate} shouldUpdate={shouldUpdate}
/> />
</div> </div>

View file

@ -41,10 +41,10 @@ const styles = {
// themeStyles returns the styles generated from the theme for the component. // themeStyles returns the styles generated from the theme for the component.
const themeStyles = theme => ({ const themeStyles = theme => ({
list: { list: {
background: theme.palette.background.appBar, background: theme.palette.grey[900],
}, },
listItem: { listItem: {
minWidth: theme.spacing.unit * 3, minWidth: theme.spacing.unit * 7,
}, },
icon: { icon: {
fontSize: theme.spacing.unit * 3, fontSize: theme.spacing.unit * 3,

File diff suppressed because it is too large Load diff

View file

@ -15,11 +15,11 @@
"css-loader": "^0.28.9", "css-loader": "^0.28.9",
"eslint": "^4.16.0", "eslint": "^4.16.0",
"eslint-config-airbnb": "^16.1.0", "eslint-config-airbnb": "^16.1.0",
"eslint-loader": "^1.9.0", "eslint-loader": "^2.0.0",
"eslint-plugin-flowtype": "^2.41.0",
"eslint-plugin-import": "^2.8.0", "eslint-plugin-import": "^2.8.0",
"eslint-plugin-jsx-a11y": "^6.0.3", "eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-react": "^7.5.1", "eslint-plugin-react": "^7.5.1",
"eslint-plugin-flowtype": "^2.41.0",
"file-loader": "^1.1.6", "file-loader": "^1.1.6",
"flow-bin": "^0.63.1", "flow-bin": "^0.63.1",
"flow-bin-loader": "^1.0.2", "flow-bin-loader": "^1.0.2",
@ -35,6 +35,13 @@
"style-loader": "^0.19.1", "style-loader": "^0.19.1",
"url": "^0.11.0", "url": "^0.11.0",
"url-loader": "^0.6.2", "url-loader": "^0.6.2",
"webpack": "^3.10.0" "webpack": "^3.10.0",
"webpack-dev-server": "^2.11.1"
},
"scripts": {
"build": "NODE_ENV=production webpack",
"stats": "webpack --profile --json > stats.json",
"dev": "webpack-dev-server --port 8081",
"flow": "flow-typed install"
} }
} }

View file

@ -26,22 +26,6 @@ export type Content = {
logs: Logs, logs: Logs,
}; };
export type General = {
version: ?string,
commit: ?string,
};
export type Home = {
activeMemory: ChartEntries,
virtualMemory: ChartEntries,
networkIngress: ChartEntries,
networkEgress: ChartEntries,
processCPU: ChartEntries,
systemCPU: ChartEntries,
diskRead: ChartEntries,
diskWrite: ChartEntries,
};
export type ChartEntries = Array<ChartEntry>; export type ChartEntries = Array<ChartEntry>;
export type ChartEntry = { export type ChartEntry = {
@ -49,6 +33,15 @@ export type ChartEntry = {
value: number, value: number,
}; };
export type General = {
version: ?string,
commit: ?string,
};
export type Home = {
/* TODO (kurkomisi) */
};
export type Chain = { export type Chain = {
/* TODO (kurkomisi) */ /* TODO (kurkomisi) */
}; };
@ -62,7 +55,14 @@ export type Network = {
}; };
export type System = { export type System = {
/* TODO (kurkomisi) */ activeMemory: ChartEntries,
virtualMemory: ChartEntries,
networkIngress: ChartEntries,
networkEgress: ChartEntries,
processCPU: ChartEntries,
systemCPU: ChartEntries,
diskRead: ChartEntries,
diskWrite: ChartEntries,
}; };
export type Logs = { export type Logs = {

View file

@ -32,6 +32,9 @@ module.exports = {
mangle: false, mangle: false,
beautify: true, beautify: true,
}), }),
new webpack.DefinePlugin({
PROD: process.env.NODE_ENV === 'production',
}),
], ],
module: { module: {
rules: [ rules: [

6551
dashboard/assets/yarn.lock Normal file

File diff suppressed because it is too large Load diff

View file

@ -38,8 +38,4 @@ type Config struct {
// Refresh is the refresh rate of the data updates, the chartEntry will be collected this often. // Refresh is the refresh rate of the data updates, the chartEntry will be collected this often.
Refresh time.Duration `toml:",omitempty"` Refresh time.Duration `toml:",omitempty"`
// Assets offers a possibility to manually set the dashboard website's location on the server side.
// It is useful for debugging, avoids the repeated generation of the binary.
Assets string `toml:",omitempty"`
} }

View file

@ -16,19 +16,17 @@
package dashboard package dashboard
//go:generate npm --prefix ./assets install //go:generate yarn --cwd ./assets install
//go:generate ./assets/node_modules/.bin/webpack --config ./assets/webpack.config.js --context ./assets //go:generate yarn --cwd ./assets build
//go:generate go-bindata -nometadata -o assets.go -prefix assets -nocompress -pkg dashboard assets/dashboard.html assets/bundle.js //go:generate go-bindata -nometadata -o assets.go -prefix assets -nocompress -pkg dashboard assets/index.html assets/bundle.js
//go:generate sh -c "sed 's#var _bundleJs#//nolint:misspell\\\n&#' assets.go > assets.go.tmp && mv assets.go.tmp assets.go" //go:generate sh -c "sed 's#var _bundleJs#//nolint:misspell\\\n&#' assets.go > assets.go.tmp && mv assets.go.tmp assets.go"
//go:generate sh -c "sed 's#var _dashboardHtml#//nolint:misspell\\\n&#' assets.go > assets.go.tmp && mv assets.go.tmp assets.go" //go:generate sh -c "sed 's#var _indexHtml#//nolint:misspell\\\n&#' assets.go > assets.go.tmp && mv assets.go.tmp assets.go"
//go:generate gofmt -w -s assets.go //go:generate gofmt -w -s assets.go
import ( import (
"fmt" "fmt"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"path/filepath"
"runtime" "runtime"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -62,7 +60,7 @@ type Dashboard struct {
listener net.Listener listener net.Listener
conns map[uint32]*client // Currently live websocket connections conns map[uint32]*client // Currently live websocket connections
charts *HomeMessage charts *SystemMessage
commit string commit string
lock sync.RWMutex // Lock protecting the dashboard's internals lock sync.RWMutex // Lock protecting the dashboard's internals
@ -84,7 +82,7 @@ func New(config *Config, commit string) (*Dashboard, error) {
conns: make(map[uint32]*client), conns: make(map[uint32]*client),
config: config, config: config,
quit: make(chan chan error), quit: make(chan chan error),
charts: &HomeMessage{ charts: &SystemMessage{
ActiveMemory: emptyChartEntries(now, activeMemorySampleLimit, config.Refresh), ActiveMemory: emptyChartEntries(now, activeMemorySampleLimit, config.Refresh),
VirtualMemory: emptyChartEntries(now, virtualMemorySampleLimit, config.Refresh), VirtualMemory: emptyChartEntries(now, virtualMemorySampleLimit, config.Refresh),
NetworkIngress: emptyChartEntries(now, networkIngressSampleLimit, config.Refresh), NetworkIngress: emptyChartEntries(now, networkIngressSampleLimit, config.Refresh),
@ -180,18 +178,7 @@ func (db *Dashboard) webHandler(w http.ResponseWriter, r *http.Request) {
path := r.URL.String() path := r.URL.String()
if path == "/" { if path == "/" {
path = "/dashboard.html" path = "/index.html"
}
// If the path of the assets is manually set
if db.config.Assets != "" {
blob, err := ioutil.ReadFile(filepath.Join(db.config.Assets, path))
if err != nil {
log.Warn("Failed to read file", "path", path, "err", err)
http.Error(w, "not found", http.StatusNotFound)
return
}
w.Write(blob)
return
} }
blob, err := Asset(path[1:]) blob, err := Asset(path[1:])
if err != nil { if err != nil {
@ -241,7 +228,7 @@ func (db *Dashboard) apiHandler(conn *websocket.Conn) {
Version: fmt.Sprintf("v%d.%d.%d%s", params.VersionMajor, params.VersionMinor, params.VersionPatch, versionMeta), Version: fmt.Sprintf("v%d.%d.%d%s", params.VersionMajor, params.VersionMinor, params.VersionPatch, versionMeta),
Commit: db.commit, Commit: db.commit,
}, },
Home: &HomeMessage{ System: &SystemMessage{
ActiveMemory: db.charts.ActiveMemory, ActiveMemory: db.charts.ActiveMemory,
VirtualMemory: db.charts.VirtualMemory, VirtualMemory: db.charts.VirtualMemory,
NetworkIngress: db.charts.NetworkIngress, NetworkIngress: db.charts.NetworkIngress,
@ -277,12 +264,14 @@ func (db *Dashboard) collectData() {
systemCPUUsage := gosigar.Cpu{} systemCPUUsage := gosigar.Cpu{}
systemCPUUsage.Get() systemCPUUsage.Get()
var ( var (
mem runtime.MemStats
prevNetworkIngress = metrics.DefaultRegistry.Get("p2p/InboundTraffic").(metrics.Meter).Count() prevNetworkIngress = metrics.DefaultRegistry.Get("p2p/InboundTraffic").(metrics.Meter).Count()
prevNetworkEgress = metrics.DefaultRegistry.Get("p2p/OutboundTraffic").(metrics.Meter).Count() prevNetworkEgress = metrics.DefaultRegistry.Get("p2p/OutboundTraffic").(metrics.Meter).Count()
prevProcessCPUTime = getProcessCPUTime() prevProcessCPUTime = getProcessCPUTime()
prevSystemCPUUsage = systemCPUUsage prevSystemCPUUsage = systemCPUUsage
prevDiskRead = metrics.DefaultRegistry.Get("eth/db/chaindata/compact/input").(metrics.Meter).Count() prevDiskRead = metrics.DefaultRegistry.Get("eth/db/chaindata/disk/read").(metrics.Meter).Count()
prevDiskWrite = metrics.DefaultRegistry.Get("eth/db/chaindata/compact/output").(metrics.Meter).Count() prevDiskWrite = metrics.DefaultRegistry.Get("eth/db/chaindata/disk/write").(metrics.Meter).Count()
frequency = float64(db.config.Refresh / time.Second) frequency = float64(db.config.Refresh / time.Second)
numCPU = float64(runtime.NumCPU()) numCPU = float64(runtime.NumCPU())
@ -300,13 +289,13 @@ func (db *Dashboard) collectData() {
curNetworkEgress = metrics.DefaultRegistry.Get("p2p/OutboundTraffic").(metrics.Meter).Count() curNetworkEgress = metrics.DefaultRegistry.Get("p2p/OutboundTraffic").(metrics.Meter).Count()
curProcessCPUTime = getProcessCPUTime() curProcessCPUTime = getProcessCPUTime()
curSystemCPUUsage = systemCPUUsage curSystemCPUUsage = systemCPUUsage
curDiskRead = metrics.DefaultRegistry.Get("eth/db/chaindata/compact/input").(metrics.Meter).Count() curDiskRead = metrics.DefaultRegistry.Get("eth/db/chaindata/disk/read").(metrics.Meter).Count()
curDiskWrite = metrics.DefaultRegistry.Get("eth/db/chaindata/compact/output").(metrics.Meter).Count() curDiskWrite = metrics.DefaultRegistry.Get("eth/db/chaindata/disk/write").(metrics.Meter).Count()
deltaNetworkIngress = float64(curNetworkIngress - prevNetworkIngress) deltaNetworkIngress = float64(curNetworkIngress - prevNetworkIngress)
deltaNetworkEgress = float64(curNetworkEgress - prevNetworkEgress) deltaNetworkEgress = float64(curNetworkEgress - prevNetworkEgress)
deltaProcessCPUTime = curProcessCPUTime - prevProcessCPUTime deltaProcessCPUTime = curProcessCPUTime - prevProcessCPUTime
deltaSystemCPUUsage = systemCPUUsage.Delta(prevSystemCPUUsage) deltaSystemCPUUsage = curSystemCPUUsage.Delta(prevSystemCPUUsage)
deltaDiskRead = curDiskRead - prevDiskRead deltaDiskRead = curDiskRead - prevDiskRead
deltaDiskWrite = curDiskWrite - prevDiskWrite deltaDiskWrite = curDiskWrite - prevDiskWrite
) )
@ -319,7 +308,6 @@ func (db *Dashboard) collectData() {
now := time.Now() now := time.Now()
var mem runtime.MemStats
runtime.ReadMemStats(&mem) runtime.ReadMemStats(&mem)
activeMemory := &ChartEntry{ activeMemory := &ChartEntry{
Time: now, Time: now,
@ -363,7 +351,7 @@ func (db *Dashboard) collectData() {
db.charts.DiskWrite = append(db.charts.DiskRead[1:], diskWrite) db.charts.DiskWrite = append(db.charts.DiskRead[1:], diskWrite)
db.sendToAll(&Message{ db.sendToAll(&Message{
Home: &HomeMessage{ System: &SystemMessage{
ActiveMemory: ChartEntries{activeMemory}, ActiveMemory: ChartEntries{activeMemory},
VirtualMemory: ChartEntries{virtualMemory}, VirtualMemory: ChartEntries{virtualMemory},
NetworkIngress: ChartEntries{networkIngress}, NetworkIngress: ChartEntries{networkIngress},

View file

@ -28,27 +28,20 @@ type Message struct {
Logs *LogsMessage `json:"logs,omitempty"` Logs *LogsMessage `json:"logs,omitempty"`
} }
type ChartEntries []*ChartEntry
type ChartEntry struct {
Time time.Time `json:"time,omitempty"`
Value float64 `json:"value,omitempty"`
}
type GeneralMessage struct { type GeneralMessage struct {
Version string `json:"version,omitempty"` Version string `json:"version,omitempty"`
Commit string `json:"commit,omitempty"` Commit string `json:"commit,omitempty"`
} }
type HomeMessage struct { type HomeMessage struct {
ActiveMemory ChartEntries `json:"activeMemory,omitempty"` /* TODO (kurkomisi) */
VirtualMemory ChartEntries `json:"virtualMemory,omitempty"`
NetworkIngress ChartEntries `json:"networkIngress,omitempty"`
NetworkEgress ChartEntries `json:"networkEgress,omitempty"`
ProcessCPU ChartEntries `json:"processCPU,omitempty"`
SystemCPU ChartEntries `json:"systemCPU,omitempty"`
DiskRead ChartEntries `json:"diskRead,omitempty"`
DiskWrite ChartEntries `json:"diskWrite,omitempty"`
}
type ChartEntries []*ChartEntry
type ChartEntry struct {
Time time.Time `json:"time,omitempty"`
Value float64 `json:"value,omitempty"`
} }
type ChainMessage struct { type ChainMessage struct {
@ -64,7 +57,14 @@ type NetworkMessage struct {
} }
type SystemMessage struct { type SystemMessage struct {
/* TODO (kurkomisi) */ ActiveMemory ChartEntries `json:"activeMemory,omitempty"`
VirtualMemory ChartEntries `json:"virtualMemory,omitempty"`
NetworkIngress ChartEntries `json:"networkIngress,omitempty"`
NetworkEgress ChartEntries `json:"networkEgress,omitempty"`
ProcessCPU ChartEntries `json:"processCPU,omitempty"`
SystemCPU ChartEntries `json:"systemCPU,omitempty"`
DiskRead ChartEntries `json:"diskRead,omitempty"`
DiskWrite ChartEntries `json:"diskWrite,omitempty"`
} }
type LogsMessage struct { type LogsMessage struct {

View file

@ -1296,6 +1296,14 @@ func (d *Downloader) processHeaders(origin uint64, pivot uint64, td *big.Int) er
headers = headers[limit:] headers = headers[limit:]
origin += uint64(limit) origin += uint64(limit)
} }
// Update the highest block number we know if a higher one is found.
d.syncStatsLock.Lock()
if d.syncStatsChainHeight < origin {
d.syncStatsChainHeight = origin - 1
}
d.syncStatsLock.Unlock()
// Signal the content downloaders of the availablility of new tasks // Signal the content downloaders of the availablility of new tasks
for _, ch := range []chan bool{d.bodyWakeCh, d.receiptWakeCh} { for _, ch := range []chan bool{d.bodyWakeCh, d.receiptWakeCh} {
select { select {

View file

@ -633,7 +633,7 @@ func (f *Fetcher) enqueue(peer string, block *types.Block) {
} }
// insert spawns a new goroutine to run a block insertion into the chain. If the // insert spawns a new goroutine to run a block insertion into the chain. If the
// block's number is at the same height as the current import phase, if updates // block's number is at the same height as the current import phase, it updates
// the phase states accordingly. // the phase states accordingly.
func (f *Fetcher) insert(peer string, block *types.Block) { func (f *Fetcher) insert(peer string, block *types.Block) {
hash := block.Hash() hash := block.Hash()

View file

@ -188,6 +188,14 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
atomic.StoreUint32(&pm.fastSync, 1) atomic.StoreUint32(&pm.fastSync, 1)
mode = downloader.FastSync mode = downloader.FastSync
} }
if mode == downloader.FastSync {
// Make sure the peer's total difficulty we are synchronizing is higher.
if pm.blockchain.GetTdByHash(pm.blockchain.CurrentFastBlock().Hash()).Cmp(pTd) >= 0 {
return
}
}
// Run the sync cycle, and disable fast sync if we've went past the pivot block // Run the sync cycle, and disable fast sync if we've went past the pivot block
if err := pm.downloader.Synchronise(peer.id, pHead, pTd, mode); err != nil { if err := pm.downloader.Synchronise(peer.id, pHead, pTd, mode); err != nil {
return return

View file

@ -37,15 +37,11 @@ type LDBDatabase struct {
fn string // filename for reporting fn string // filename for reporting
db *leveldb.DB // LevelDB instance db *leveldb.DB // LevelDB instance
getTimer metrics.Timer // Timer for measuring the database get request counts and latencies
putTimer metrics.Timer // Timer for measuring the database put request counts and latencies
delTimer metrics.Timer // Timer for measuring the database delete request counts and latencies
missMeter metrics.Meter // Meter for measuring the missed database get requests
readMeter metrics.Meter // Meter for measuring the database get request data usage
writeMeter metrics.Meter // Meter for measuring the database put request data usage
compTimeMeter metrics.Meter // Meter for measuring the total time spent in database compaction compTimeMeter metrics.Meter // Meter for measuring the total time spent in database compaction
compReadMeter metrics.Meter // Meter for measuring the data read during compaction compReadMeter metrics.Meter // Meter for measuring the data read during compaction
compWriteMeter metrics.Meter // Meter for measuring the data written during compaction compWriteMeter metrics.Meter // Meter for measuring the data written during compaction
diskReadMeter metrics.Meter // Meter for measuring the effective amount of data read
diskWriteMeter metrics.Meter // Meter for measuring the effective amount of data written
quitLock sync.Mutex // Mutex protecting the quit channel access quitLock sync.Mutex // Mutex protecting the quit channel access
quitChan chan chan error // Quit channel to stop the metrics collection before closing the database quitChan chan chan error // Quit channel to stop the metrics collection before closing the database
@ -94,16 +90,9 @@ func (db *LDBDatabase) Path() string {
// Put puts the given key / value to the queue // Put puts the given key / value to the queue
func (db *LDBDatabase) Put(key []byte, value []byte) error { func (db *LDBDatabase) Put(key []byte, value []byte) error {
// Measure the database put latency, if requested
if db.putTimer != nil {
defer db.putTimer.UpdateSince(time.Now())
}
// Generate the data to write to disk, update the meter and write // Generate the data to write to disk, update the meter and write
//value = rle.Compress(value) //value = rle.Compress(value)
if db.writeMeter != nil {
db.writeMeter.Mark(int64(len(value)))
}
return db.db.Put(key, value, nil) return db.db.Put(key, value, nil)
} }
@ -113,32 +102,17 @@ func (db *LDBDatabase) Has(key []byte) (bool, error) {
// Get returns the given key if it's present. // Get returns the given key if it's present.
func (db *LDBDatabase) Get(key []byte) ([]byte, error) { func (db *LDBDatabase) Get(key []byte) ([]byte, error) {
// Measure the database get latency, if requested
if db.getTimer != nil {
defer db.getTimer.UpdateSince(time.Now())
}
// Retrieve the key and increment the miss counter if not found // Retrieve the key and increment the miss counter if not found
dat, err := db.db.Get(key, nil) dat, err := db.db.Get(key, nil)
if err != nil { if err != nil {
if db.missMeter != nil {
db.missMeter.Mark(1)
}
return nil, err return nil, err
} }
// Otherwise update the actually retrieved amount of data
if db.readMeter != nil {
db.readMeter.Mark(int64(len(dat)))
}
return dat, nil return dat, nil
//return rle.Decompress(dat) //return rle.Decompress(dat)
} }
// Delete deletes the key from the queue and database // Delete deletes the key from the queue and database
func (db *LDBDatabase) Delete(key []byte) error { func (db *LDBDatabase) Delete(key []byte) error {
// Measure the database delete latency, if requested
if db.delTimer != nil {
defer db.delTimer.UpdateSince(time.Now())
}
// Execute the actual operation // Execute the actual operation
return db.db.Delete(key, nil) return db.db.Delete(key, nil)
} }
@ -178,15 +152,11 @@ func (db *LDBDatabase) Meter(prefix string) {
return return
} }
// Initialize all the metrics collector at the requested prefix // Initialize all the metrics collector at the requested prefix
db.getTimer = metrics.NewRegisteredTimer(prefix+"user/gets", nil)
db.putTimer = metrics.NewRegisteredTimer(prefix+"user/puts", nil)
db.delTimer = metrics.NewRegisteredTimer(prefix+"user/dels", nil)
db.missMeter = metrics.NewRegisteredMeter(prefix+"user/misses", nil)
db.readMeter = metrics.NewRegisteredMeter(prefix+"user/reads", nil)
db.writeMeter = metrics.NewRegisteredMeter(prefix+"user/writes", nil)
db.compTimeMeter = metrics.NewRegisteredMeter(prefix+"compact/time", nil) db.compTimeMeter = metrics.NewRegisteredMeter(prefix+"compact/time", nil)
db.compReadMeter = metrics.NewRegisteredMeter(prefix+"compact/input", nil) db.compReadMeter = metrics.NewRegisteredMeter(prefix+"compact/input", nil)
db.compWriteMeter = metrics.NewRegisteredMeter(prefix+"compact/output", nil) db.compWriteMeter = metrics.NewRegisteredMeter(prefix+"compact/output", nil)
db.diskReadMeter = metrics.NewRegisteredMeter(prefix+"disk/read", nil)
db.diskWriteMeter = metrics.NewRegisteredMeter(prefix+"disk/write", nil)
// Create a quit channel for the periodic collector and run it // Create a quit channel for the periodic collector and run it
db.quitLock.Lock() db.quitLock.Lock()
@ -207,12 +177,17 @@ func (db *LDBDatabase) Meter(prefix string) {
// 1 | 85 | 109.27913 | 28.09293 | 213.92493 | 214.26294 // 1 | 85 | 109.27913 | 28.09293 | 213.92493 | 214.26294
// 2 | 523 | 1000.37159 | 7.26059 | 66.86342 | 66.77884 // 2 | 523 | 1000.37159 | 7.26059 | 66.86342 | 66.77884
// 3 | 570 | 1113.18458 | 0.00000 | 0.00000 | 0.00000 // 3 | 570 | 1113.18458 | 0.00000 | 0.00000 | 0.00000
//
// This is how the iostats look like (currently):
// Read(MB):3895.04860 Write(MB):3654.64712
func (db *LDBDatabase) meter(refresh time.Duration) { func (db *LDBDatabase) meter(refresh time.Duration) {
// Create the counters to store current and previous values // Create the counters to store current and previous compaction values
counters := make([][]float64, 2) compactions := make([][]float64, 2)
for i := 0; i < 2; i++ { for i := 0; i < 2; i++ {
counters[i] = make([]float64, 3) compactions[i] = make([]float64, 3)
} }
// Create storage for iostats.
var iostats [2]float64
// Iterate ad infinitum and collect the stats // Iterate ad infinitum and collect the stats
for i := 1; ; i++ { for i := 1; ; i++ {
// Retrieve the database stats // Retrieve the database stats
@ -233,8 +208,8 @@ func (db *LDBDatabase) meter(refresh time.Duration) {
lines = lines[3:] lines = lines[3:]
// Iterate over all the table rows, and accumulate the entries // Iterate over all the table rows, and accumulate the entries
for j := 0; j < len(counters[i%2]); j++ { for j := 0; j < len(compactions[i%2]); j++ {
counters[i%2][j] = 0 compactions[i%2][j] = 0
} }
for _, line := range lines { for _, line := range lines {
parts := strings.Split(line, "|") parts := strings.Split(line, "|")
@ -247,19 +222,60 @@ func (db *LDBDatabase) meter(refresh time.Duration) {
db.log.Error("Compaction entry parsing failed", "err", err) db.log.Error("Compaction entry parsing failed", "err", err)
return return
} }
counters[i%2][idx] += value compactions[i%2][idx] += value
} }
} }
// Update all the requested meters // Update all the requested meters
if db.compTimeMeter != nil { if db.compTimeMeter != nil {
db.compTimeMeter.Mark(int64((counters[i%2][0] - counters[(i-1)%2][0]) * 1000 * 1000 * 1000)) db.compTimeMeter.Mark(int64((compactions[i%2][0] - compactions[(i-1)%2][0]) * 1000 * 1000 * 1000))
} }
if db.compReadMeter != nil { if db.compReadMeter != nil {
db.compReadMeter.Mark(int64((counters[i%2][1] - counters[(i-1)%2][1]) * 1024 * 1024)) db.compReadMeter.Mark(int64((compactions[i%2][1] - compactions[(i-1)%2][1]) * 1024 * 1024))
} }
if db.compWriteMeter != nil { if db.compWriteMeter != nil {
db.compWriteMeter.Mark(int64((counters[i%2][2] - counters[(i-1)%2][2]) * 1024 * 1024)) db.compWriteMeter.Mark(int64((compactions[i%2][2] - compactions[(i-1)%2][2]) * 1024 * 1024))
} }
// Retrieve the database iostats.
ioStats, err := db.db.GetProperty("leveldb.iostats")
if err != nil {
db.log.Error("Failed to read database iostats", "err", err)
return
}
parts := strings.Split(ioStats, " ")
if len(parts) < 2 {
db.log.Error("Bad syntax of ioStats", "ioStats", ioStats)
return
}
r := strings.Split(parts[0], ":")
if len(r) < 2 {
db.log.Error("Bad syntax of read entry", "entry", parts[0])
return
}
read, err := strconv.ParseFloat(r[1], 64)
if err != nil {
db.log.Error("Read entry parsing failed", "err", err)
return
}
w := strings.Split(parts[1], ":")
if len(w) < 2 {
db.log.Error("Bad syntax of write entry", "entry", parts[1])
return
}
write, err := strconv.ParseFloat(w[1], 64)
if err != nil {
db.log.Error("Write entry parsing failed", "err", err)
return
}
if db.diskReadMeter != nil {
db.diskReadMeter.Mark(int64((read - iostats[0]) * 1024 * 1024))
}
if db.diskWriteMeter != nil {
db.diskWriteMeter.Mark(int64((write - iostats[1]) * 1024 * 1024))
}
iostats[0] = read
iostats[1] = write
// Sleep a bit, then repeat the stats collection // Sleep a bit, then repeat the stats collection
select { select {
case errc := <-db.quitChan: case errc := <-db.quitChan:

View file

@ -1337,10 +1337,10 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, sendArgs SendTxAr
if pFrom, err := types.Sender(signer, p); err == nil && pFrom == sendArgs.From && signer.Hash(p) == wantSigHash { if pFrom, err := types.Sender(signer, p); err == nil && pFrom == sendArgs.From && signer.Hash(p) == wantSigHash {
// Match. Re-sign and send the transaction. // Match. Re-sign and send the transaction.
if gasPrice != nil { if gasPrice != nil && (*big.Int)(gasPrice).Sign() != 0 {
sendArgs.GasPrice = gasPrice sendArgs.GasPrice = gasPrice
} }
if gasLimit != nil { if gasLimit != nil && *gasLimit != 0 {
sendArgs.Gas = gasLimit sendArgs.Gas = gasLimit
} }
signedTx, err := s.sign(sendArgs.From, sendArgs.toTransaction()) signedTx, err := s.sign(sendArgs.From, sendArgs.toTransaction())

View file

@ -144,7 +144,9 @@ func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, num
genesis := core.GetCanonicalHash(odr.Database(), 0) genesis := core.GetCanonicalHash(odr.Database(), 0)
config, _ := core.GetChainConfig(odr.Database(), genesis) config, _ := core.GetChainConfig(odr.Database(), genesis)
core.SetReceiptsData(config, block, receipts) if err := core.SetReceiptsData(config, block, receipts); err != nil {
return nil, err
}
core.WriteBlockReceipts(odr.Database(), hash, number, receipts) core.WriteBlockReceipts(odr.Database(), hash, number, receipts)
} }
return receipts, nil return receipts, nil

View file

@ -21,10 +21,10 @@ import (
) )
const ( const (
VersionMajor = 1 // Major version component of the current release VersionMajor = 1 // Major version component of the current release
VersionMinor = 8 // Minor version component of the current release VersionMinor = 8 // Minor version component of the current release
VersionPatch = 2 // Patch version component of the current release VersionPatch = 3 // Patch version component of the current release
VersionMeta = "stable" // Version metadata to append to the version string VersionMeta = "unstable" // Version metadata to append to the version string
) )
// Version holds the textual version string. // Version holds the textual version string.

View file

@ -363,7 +363,7 @@ func TestRandomData(t *testing.T) {
} }
func TestRandomBrokenData(t *testing.T) { func XTestRandomBrokenData(t *testing.T) {
sizes := []int{1, 60, 83, 179, 253, 1024, 4095, 4096, 4097, 8191, 8192, 8193, 12287, 12288, 12289, 123456, 2345678} sizes := []int{1, 60, 83, 179, 253, 1024, 4095, 4096, 4097, 8191, 8192, 8193, 12287, 12288, 12289, 123456, 2345678}
tester := &chunkerTester{t: t} tester := &chunkerTester{t: t}
chunker := NewTreeChunker(NewChunkerParams()) chunker := NewTreeChunker(NewChunkerParams())

View file

@ -906,6 +906,8 @@ func (db *DB) GetSnapshot() (*Snapshot, error) {
// Returns the number of files at level 'n'. // Returns the number of files at level 'n'.
// leveldb.stats // leveldb.stats
// Returns statistics of the underlying DB. // Returns statistics of the underlying DB.
// leveldb.iostats
// Returns statistics of effective disk read and write.
// leveldb.writedelay // leveldb.writedelay
// Returns cumulative write delay caused by compaction. // Returns cumulative write delay caused by compaction.
// leveldb.sstables // leveldb.sstables
@ -959,6 +961,10 @@ func (db *DB) GetProperty(name string) (value string, err error) {
level, len(tables), float64(tables.size())/1048576.0, duration.Seconds(), level, len(tables), float64(tables.size())/1048576.0, duration.Seconds(),
float64(read)/1048576.0, float64(write)/1048576.0) float64(read)/1048576.0, float64(write)/1048576.0)
} }
case p == "iostats":
value = fmt.Sprintf("Read(MB):%.5f Write(MB):%.5f",
float64(db.s.stor.reads())/1048576.0,
float64(db.s.stor.writes())/1048576.0)
case p == "writedelay": case p == "writedelay":
writeDelayN, writeDelay := atomic.LoadInt32(&db.cWriteDelayN), time.Duration(atomic.LoadInt64(&db.cWriteDelay)) writeDelayN, writeDelay := atomic.LoadInt32(&db.cWriteDelayN), time.Duration(atomic.LoadInt64(&db.cWriteDelay))
value = fmt.Sprintf("DelayN:%d Delay:%s", writeDelayN, writeDelay) value = fmt.Sprintf("DelayN:%d Delay:%s", writeDelayN, writeDelay)

View file

@ -88,7 +88,7 @@ type Iterator interface {
// its contents may change on the next call to any 'seeks method'. // its contents may change on the next call to any 'seeks method'.
Key() []byte Key() []byte
// Value returns the key of the current key/value pair, or nil if done. // Value returns the value of the current key/value pair, or nil if done.
// The caller should not modify the contents of the returned slice, and // The caller should not modify the contents of the returned slice, and
// its contents may change on the next call to any 'seeks method'. // its contents may change on the next call to any 'seeks method'.
Value() []byte Value() []byte

View file

@ -329,7 +329,7 @@ func (p *DB) Delete(key []byte) error {
h := p.nodeData[node+nHeight] h := p.nodeData[node+nHeight]
for i, n := range p.prevNode[:h] { for i, n := range p.prevNode[:h] {
m := n + 4 + i m := n + nNext + i
p.nodeData[m] = p.nodeData[p.nodeData[m]+nNext+i] p.nodeData[m] = p.nodeData[p.nodeData[m]+nNext+i]
} }

View file

@ -42,7 +42,7 @@ type session struct {
stTempFileNum int64 stTempFileNum int64
stSeqNum uint64 // last mem compacted seq; need external synchronization stSeqNum uint64 // last mem compacted seq; need external synchronization
stor storage.Storage stor *iStorage
storLock storage.Locker storLock storage.Locker
o *cachedOptions o *cachedOptions
icmp *iComparer icmp *iComparer
@ -68,7 +68,7 @@ func newSession(stor storage.Storage, o *opt.Options) (s *session, err error) {
return return
} }
s = &session{ s = &session{
stor: stor, stor: newIStorage(stor),
storLock: storLock, storLock: storLock,
fileRef: make(map[int64]int), fileRef: make(map[int64]int),
} }

63
vendor/github.com/syndtr/goleveldb/leveldb/storage.go generated vendored Normal file
View file

@ -0,0 +1,63 @@
package leveldb
import (
"github.com/syndtr/goleveldb/leveldb/storage"
"sync/atomic"
)
type iStorage struct {
storage.Storage
read uint64
write uint64
}
func (c *iStorage) Open(fd storage.FileDesc) (storage.Reader, error) {
r, err := c.Storage.Open(fd)
return &iStorageReader{r, c}, err
}
func (c *iStorage) Create(fd storage.FileDesc) (storage.Writer, error) {
w, err := c.Storage.Create(fd)
return &iStorageWriter{w, c}, err
}
func (c *iStorage) reads() uint64 {
return atomic.LoadUint64(&c.read)
}
func (c *iStorage) writes() uint64 {
return atomic.LoadUint64(&c.write)
}
// newIStorage returns the given storage wrapped by iStorage.
func newIStorage(s storage.Storage) *iStorage {
return &iStorage{s, 0, 0}
}
type iStorageReader struct {
storage.Reader
c *iStorage
}
func (r *iStorageReader) Read(p []byte) (n int, err error) {
n, err = r.Reader.Read(p)
atomic.AddUint64(&r.c.read, uint64(n))
return n, err
}
func (r *iStorageReader) ReadAt(p []byte, off int64) (n int, err error) {
n, err = r.Reader.ReadAt(p, off)
atomic.AddUint64(&r.c.read, uint64(n))
return n, err
}
type iStorageWriter struct {
storage.Writer
c *iStorage
}
func (w *iStorageWriter) Write(p []byte) (n int, err error) {
n, err = w.Writer.Write(p)
atomic.AddUint64(&w.c.write, uint64(n))
return n, err
}

View file

@ -8,7 +8,6 @@ package storage
import ( import (
"os" "os"
"path/filepath"
) )
type plan9FileLock struct { type plan9FileLock struct {
@ -48,8 +47,7 @@ func rename(oldpath, newpath string) error {
} }
} }
_, fname := filepath.Split(newpath) return os.Rename(oldpath, newpath)
return os.Rename(oldpath, fname)
} }
func syncDir(name string) error { func syncDir(name string) error {

View file

@ -19,7 +19,7 @@ var (
// Releaser is the interface that wraps the basic Release method. // Releaser is the interface that wraps the basic Release method.
type Releaser interface { type Releaser interface {
// Release releases associated resources. Release should always success // Release releases associated resources. Release should always success
// and can be called multipe times without causing error. // and can be called multiple times without causing error.
Release() Release()
} }

View file

@ -1,8 +1,8 @@
package duktape package duktape
/* /*
#cgo !windows CFLAGS: -std=c99 -O3 -Wall -fomit-frame-pointer -fstrict-aliasing #cgo !windows CFLAGS: -std=c99 -O3 -Wall -Wno-unused-value -fomit-frame-pointer -fstrict-aliasing
#cgo windows CFLAGS: -O3 -Wall -fomit-frame-pointer -fstrict-aliasing #cgo windows CFLAGS: -O3 -Wall -Wno-unused-value -fomit-frame-pointer -fstrict-aliasing
#include "duktape.h" #include "duktape.h"
#include "duk_logging.h" #include "duk_logging.h"

View file

@ -5,6 +5,7 @@ package duktape
#cgo windows CFLAGS: -O3 -Wall -fomit-frame-pointer -fstrict-aliasing #cgo windows CFLAGS: -O3 -Wall -fomit-frame-pointer -fstrict-aliasing
#cgo linux LDFLAGS: -lm #cgo linux LDFLAGS: -lm
#cgo freebsd LDFLAGS: -lm #cgo freebsd LDFLAGS: -lm
#cgo openbsd LDFLAGS: -lm
#include "duktape.h" #include "duktape.h"
#include "duk_logging.h" #include "duk_logging.h"

64
vendor/vendor.json vendored
View file

@ -406,76 +406,76 @@
"revisionTime": "2017-07-05T02:17:15Z" "revisionTime": "2017-07-05T02:17:15Z"
}, },
{ {
"checksumSHA1": "rpu5ZHjXlV13UKA7L1d5MTOyQwA=", "checksumSHA1": "3QsnhPTXGytTbW3uDvQLgSo9s9M=",
"path": "github.com/syndtr/goleveldb/leveldb", "path": "github.com/syndtr/goleveldb/leveldb",
"revision": "211f780988068502fe874c44dae530528ebd840f", "revision": "169b1b37be738edb2813dab48c97a549bcf99bb5",
"revisionTime": "2018-01-28T14:04:16Z" "revisionTime": "2018-03-07T11:33:52Z"
}, },
{ {
"checksumSHA1": "EKIow7XkgNdWvR/982ffIZxKG8Y=", "checksumSHA1": "EKIow7XkgNdWvR/982ffIZxKG8Y=",
"path": "github.com/syndtr/goleveldb/leveldb/cache", "path": "github.com/syndtr/goleveldb/leveldb/cache",
"revision": "b89cc31ef7977104127d34c1bd31ebd1a9db2199", "revision": "169b1b37be738edb2813dab48c97a549bcf99bb5",
"revisionTime": "2017-07-25T06:48:36Z" "revisionTime": "2018-03-07T11:33:52Z"
}, },
{ {
"checksumSHA1": "5KPgnvCPlR0ysDAqo6jApzRQ3tw=", "checksumSHA1": "5KPgnvCPlR0ysDAqo6jApzRQ3tw=",
"path": "github.com/syndtr/goleveldb/leveldb/comparer", "path": "github.com/syndtr/goleveldb/leveldb/comparer",
"revision": "b89cc31ef7977104127d34c1bd31ebd1a9db2199", "revision": "169b1b37be738edb2813dab48c97a549bcf99bb5",
"revisionTime": "2017-07-25T06:48:36Z" "revisionTime": "2018-03-07T11:33:52Z"
}, },
{ {
"checksumSHA1": "1DRAxdlWzS4U0xKN/yQ/fdNN7f0=", "checksumSHA1": "1DRAxdlWzS4U0xKN/yQ/fdNN7f0=",
"path": "github.com/syndtr/goleveldb/leveldb/errors", "path": "github.com/syndtr/goleveldb/leveldb/errors",
"revision": "b89cc31ef7977104127d34c1bd31ebd1a9db2199", "revision": "169b1b37be738edb2813dab48c97a549bcf99bb5",
"revisionTime": "2017-07-25T06:48:36Z" "revisionTime": "2018-03-07T11:33:52Z"
}, },
{ {
"checksumSHA1": "eqKeD6DS7eNCtxVYZEHHRKkyZrw=", "checksumSHA1": "eqKeD6DS7eNCtxVYZEHHRKkyZrw=",
"path": "github.com/syndtr/goleveldb/leveldb/filter", "path": "github.com/syndtr/goleveldb/leveldb/filter",
"revision": "b89cc31ef7977104127d34c1bd31ebd1a9db2199", "revision": "169b1b37be738edb2813dab48c97a549bcf99bb5",
"revisionTime": "2017-07-25T06:48:36Z" "revisionTime": "2018-03-07T11:33:52Z"
}, },
{ {
"checksumSHA1": "8dXuAVIsbtaMiGGuHjzGR6Ny/5c=", "checksumSHA1": "weSsccMav4BCerDpSLzh3mMxAYo=",
"path": "github.com/syndtr/goleveldb/leveldb/iterator", "path": "github.com/syndtr/goleveldb/leveldb/iterator",
"revision": "b89cc31ef7977104127d34c1bd31ebd1a9db2199", "revision": "169b1b37be738edb2813dab48c97a549bcf99bb5",
"revisionTime": "2017-07-25T06:48:36Z" "revisionTime": "2018-03-07T11:33:52Z"
}, },
{ {
"checksumSHA1": "gJY7bRpELtO0PJpZXgPQ2BYFJ88=", "checksumSHA1": "gJY7bRpELtO0PJpZXgPQ2BYFJ88=",
"path": "github.com/syndtr/goleveldb/leveldb/journal", "path": "github.com/syndtr/goleveldb/leveldb/journal",
"revision": "b89cc31ef7977104127d34c1bd31ebd1a9db2199", "revision": "169b1b37be738edb2813dab48c97a549bcf99bb5",
"revisionTime": "2017-07-25T06:48:36Z" "revisionTime": "2018-03-07T11:33:52Z"
}, },
{ {
"checksumSHA1": "j+uaQ6DwJ50dkIdfMQu1TXdlQcY=", "checksumSHA1": "MtYY1b2234y/MlS+djL8tXVAcQs=",
"path": "github.com/syndtr/goleveldb/leveldb/memdb", "path": "github.com/syndtr/goleveldb/leveldb/memdb",
"revision": "b89cc31ef7977104127d34c1bd31ebd1a9db2199", "revision": "169b1b37be738edb2813dab48c97a549bcf99bb5",
"revisionTime": "2017-07-25T06:48:36Z" "revisionTime": "2018-03-07T11:33:52Z"
}, },
{ {
"checksumSHA1": "UmQeotV+m8/FduKEfLOhjdp18rs=", "checksumSHA1": "UmQeotV+m8/FduKEfLOhjdp18rs=",
"path": "github.com/syndtr/goleveldb/leveldb/opt", "path": "github.com/syndtr/goleveldb/leveldb/opt",
"revision": "b89cc31ef7977104127d34c1bd31ebd1a9db2199", "revision": "169b1b37be738edb2813dab48c97a549bcf99bb5",
"revisionTime": "2017-07-25T06:48:36Z" "revisionTime": "2018-03-07T11:33:52Z"
}, },
{ {
"checksumSHA1": "tQ2AqXXAEy9icbZI9dLVdZGvWMw=", "checksumSHA1": "QCSae2ub87f8awH+PKMpd8ZYOtg=",
"path": "github.com/syndtr/goleveldb/leveldb/storage", "path": "github.com/syndtr/goleveldb/leveldb/storage",
"revision": "b89cc31ef7977104127d34c1bd31ebd1a9db2199", "revision": "169b1b37be738edb2813dab48c97a549bcf99bb5",
"revisionTime": "2017-07-25T06:48:36Z" "revisionTime": "2018-03-07T11:33:52Z"
}, },
{ {
"checksumSHA1": "gWFPMz8OQeul0t54RM66yMTX49g=", "checksumSHA1": "gWFPMz8OQeul0t54RM66yMTX49g=",
"path": "github.com/syndtr/goleveldb/leveldb/table", "path": "github.com/syndtr/goleveldb/leveldb/table",
"revision": "b89cc31ef7977104127d34c1bd31ebd1a9db2199", "revision": "169b1b37be738edb2813dab48c97a549bcf99bb5",
"revisionTime": "2017-07-25T06:48:36Z" "revisionTime": "2018-03-07T11:33:52Z"
}, },
{ {
"checksumSHA1": "4zil8Gwg8VPkDn1YzlgCvtukJFU=", "checksumSHA1": "V/Dh7NV0/fy/5jX1KaAjmGcNbzI=",
"path": "github.com/syndtr/goleveldb/leveldb/util", "path": "github.com/syndtr/goleveldb/leveldb/util",
"revision": "b89cc31ef7977104127d34c1bd31ebd1a9db2199", "revision": "169b1b37be738edb2813dab48c97a549bcf99bb5",
"revisionTime": "2017-07-25T06:48:36Z" "revisionTime": "2018-03-07T11:33:52Z"
}, },
{ {
"checksumSHA1": "TT1rac6kpQp2vz24m5yDGUNQ/QQ=", "checksumSHA1": "TT1rac6kpQp2vz24m5yDGUNQ/QQ=",
@ -742,10 +742,10 @@
"revisionTime": "2016-06-21T03:49:01Z" "revisionTime": "2016-06-21T03:49:01Z"
}, },
{ {
"checksumSHA1": "YvuEbc0a032zr+BTp/YbBQojiuY=", "checksumSHA1": "vndxs5Tv09/8S+Dr2PBAiM557lI=",
"path": "gopkg.in/olebedev/go-duktape.v3", "path": "gopkg.in/olebedev/go-duktape.v3",
"revision": "9af39127cb024b355a6a0221769f6ddfe3f542e7", "revision": "abf0ba0be5d5d36b1f9266463cc320b9a5ab224e",
"revisionTime": "2017-12-20T12:19:14Z" "revisionTime": "2018-03-02T12:15:09Z"
}, },
{ {
"checksumSHA1": "4BwmmgQUhWtizsR2soXND0nqZ1I=", "checksumSHA1": "4BwmmgQUhWtizsR2soXND0nqZ1I=",

View file

@ -269,3 +269,11 @@ func TopicToBloom(topic TopicType) []byte {
} }
return b return b
} }
// GetEnvelope retrieves an envelope from the message queue by its hash.
// It returns nil if the envelope can not be found.
func (w *Whisper) GetEnvelope(hash common.Hash) *Envelope {
w.poolMu.RLock()
defer w.poolMu.RUnlock()
return w.envelopes[hash]
}