Merge branch 'master' into master

This commit is contained in:
lhendre 2018-09-25 13:09:44 -04:00 committed by GitHub
commit c9daec2cbe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
315 changed files with 9244 additions and 6857 deletions

View file

@ -1,16 +1,40 @@
# Contributing
Thank you for considering to help out with the source code! We welcome
contributions from anyone on the internet, and are grateful for even the
smallest of fixes!
If you'd like to contribute to go-ethereum, please fork, fix, commit and send a
pull request for the maintainers to review and merge into the main code base. If
you wish to submit more complex changes though, please check up with the core
devs first on [our gitter channel](https://gitter.im/ethereum/go-ethereum) to
ensure those changes are in line with the general philosophy of the project
and/or get some early feedback which can make both your efforts much lighter as
well as our review and merge procedures quick and simple.
## Coding guidelines
Please make sure your contributions adhere to our coding guidelines:
* Code must adhere to the official Go
[formatting](https://golang.org/doc/effective_go.html#formatting) guidelines
(i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
* Code must be documented adhering to the official Go
[commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
* Pull requests need to be based on and opened against the `master` branch.
* Commit messages should be prefixed with the package(s) they modify.
* E.g. "eth, rpc: make trace configs optional"
## Can I have feature X ## Can I have feature X
Before you do a feature request please check and make sure that it isn't possible Before you submit a feature request, please check and make sure that it isn't
through some other means. The JavaScript enabled console is a powerful feature possible through some other means. The JavaScript-enabled console is a powerful
in the right hands. Please check our [Wiki page](https://github.com/ethereum/go-ethereum/wiki) for more info feature in the right hands. Please check our
[Wiki page](https://github.com/ethereum/go-ethereum/wiki) for more info
and help. and help.
## Contributing ## Configuration, dependencies, and tests
If you'd like to contribute to go-ethereum please fork, fix, commit and Please see the [Developers' Guide](https://github.com/ethereum/go-ethereum/wiki/Developers'-Guide)
send a pull request. Commits which do not comply with the coding standards for more details on configuring your environment, managing project dependencies
are ignored (use gofmt!). and testing procedures.
See [Developers' Guide](https://github.com/ethereum/go-ethereum/wiki/Developers'-Guide)
for more details on configuring your environment, testing, and
dependency management.

View file

@ -7,5 +7,5 @@ closeComment: >
This issue has been automatically closed because there has been no response This issue has been automatically closed because there has been no response
to our request for more information from the original author. With only the to our request for more information from the original author. With only the
information that is currently in the issue, we don't have enough information information that is currently in the issue, we don't have enough information
to take action. Please reach out if you have or find the answers we need so to take action. Please reach out if you have more relevant information or
that we can investigate further. answers to our questions so that we can investigate further.

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 $TEST_PACKAGES - go run build/ci.go test -coverage $TEST_PACKAGES
# 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 $TEST_PACKAGES - go run build/ci.go test -coverage $TEST_PACKAGES
# These are the latest Go versions.
- os: linux
dist: trusty
sudo: required
go: 1.11.x
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 $TEST_PACKAGES
- os: osx - os: osx
go: 1.10.x go: 1.11.x
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
- go run build/ci.go install - go run build/ci.go install
@ -36,7 +47,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.10.x go: 1.11.x
env: env:
- lint - lint
git: git:
@ -47,7 +58,7 @@ matrix:
# This builder does the Ubuntu PPA upload # This builder does the Ubuntu PPA upload
- os: linux - os: linux
dist: trusty dist: trusty
go: 1.10.x go: 1.11.x
env: env:
- ubuntu-ppa - ubuntu-ppa
git: git:
@ -66,7 +77,7 @@ matrix:
- os: linux - os: linux
dist: trusty dist: trusty
sudo: required sudo: required
go: 1.10.x go: 1.11.x
env: env:
- azure-linux - azure-linux
git: git:
@ -100,7 +111,7 @@ matrix:
dist: trusty dist: trusty
services: services:
- docker - docker
go: 1.10.x go: 1.11.x
env: env:
- azure-linux-mips - azure-linux-mips
git: git:
@ -144,7 +155,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.10.3.linux-amd64.tar.gz | tar -xz - curl https://storage.googleapis.com/golang/go1.11.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
@ -161,7 +172,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.10.x go: 1.11.x
env: env:
- azure-osx - azure-osx
- azure-ios - azure-ios
@ -190,7 +201,7 @@ matrix:
# This builder does the Azure archive purges to avoid accumulating junk # This builder does the Azure archive purges to avoid accumulating junk
- os: linux - os: linux
dist: trusty dist: trusty
go: 1.10.x go: 1.11.x
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.10-alpine as builder FROM golang:1.11-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.10-alpine as builder FROM golang:1.11-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

@ -7,7 +7,7 @@ https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/6874
)](https://godoc.org/github.com/ethereum/go-ethereum) )](https://godoc.org/github.com/ethereum/go-ethereum)
[![Go Report Card](https://goreportcard.com/badge/github.com/ethereum/go-ethereum)](https://goreportcard.com/report/github.com/ethereum/go-ethereum) [![Go Report Card](https://goreportcard.com/badge/github.com/ethereum/go-ethereum)](https://goreportcard.com/report/github.com/ethereum/go-ethereum)
[![Travis](https://travis-ci.org/ethereum/go-ethereum.svg?branch=master)](https://travis-ci.org/ethereum/go-ethereum) [![Travis](https://travis-ci.org/ethereum/go-ethereum.svg?branch=master)](https://travis-ci.org/ethereum/go-ethereum)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/go-ethereum?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Discord](https://img.shields.io/badge/discord-join%20chat-blue.svg)](https://discord.gg/nthXNEv)
Automated builds are available for stable releases and the unstable master branch. Automated builds are available for stable releases and the unstable master branch.
Binary archives are published at https://geth.ethereum.org/downloads/. Binary archives are published at https://geth.ethereum.org/downloads/.

View file

@ -69,7 +69,7 @@ func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBac
database := ethdb.NewMemDatabase() database := ethdb.NewMemDatabase()
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc} genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
genesis.MustCommit(database) genesis.MustCommit(database)
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil)
backend := &SimulatedBackend{ backend := &SimulatedBackend{
database: database, database: database,

View file

@ -103,7 +103,12 @@ func NewType(t string) (typ Type, err error) {
return typ, err return typ, err
} }
// parse the type and size of the abi-type. // parse the type and size of the abi-type.
parsedType := typeRegex.FindAllStringSubmatch(t, -1)[0] matches := typeRegex.FindAllStringSubmatch(t, -1)
if len(matches) == 0 {
return Type{}, fmt.Errorf("invalid type '%v'", t)
}
parsedType := matches[0]
// varSize is the size of the variable // varSize is the size of the variable
var varSize int var varSize int
if len(parsedType[3]) > 0 { if len(parsedType[3]) > 0 {

View file

@ -25,8 +25,17 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
var (
maxUint256 = big.NewInt(0).Add(
big.NewInt(0).Exp(big.NewInt(2), big.NewInt(256), nil),
big.NewInt(-1))
maxInt256 = big.NewInt(0).Add(
big.NewInt(0).Exp(big.NewInt(2), big.NewInt(255), nil),
big.NewInt(-1))
)
// reads the integer based on its kind // reads the integer based on its kind
func readInteger(kind reflect.Kind, b []byte) interface{} { func readInteger(typ byte, kind reflect.Kind, b []byte) interface{} {
switch kind { switch kind {
case reflect.Uint8: case reflect.Uint8:
return b[len(b)-1] return b[len(b)-1]
@ -45,7 +54,20 @@ func readInteger(kind reflect.Kind, b []byte) interface{} {
case reflect.Int64: case reflect.Int64:
return int64(binary.BigEndian.Uint64(b[len(b)-8:])) return int64(binary.BigEndian.Uint64(b[len(b)-8:]))
default: default:
return new(big.Int).SetBytes(b) // the only case lefts for integer is int256/uint256.
// big.SetBytes can't tell if a number is negative, positive on itself.
// On EVM, if the returned number > max int256, it is negative.
ret := new(big.Int).SetBytes(b)
if typ == UintTy {
return ret
}
if ret.Cmp(maxInt256) > 0 {
ret.Add(maxUint256, big.NewInt(0).Neg(ret))
ret.Add(ret, big.NewInt(1))
ret.Neg(ret)
}
return ret
} }
} }
@ -179,7 +201,7 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
case StringTy: // variable arrays are written at the end of the return bytes case StringTy: // variable arrays are written at the end of the return bytes
return string(output[begin : begin+end]), nil return string(output[begin : begin+end]), nil
case IntTy, UintTy: case IntTy, UintTy:
return readInteger(t.Kind, returnOutput), nil return readInteger(t.T, t.Kind, returnOutput), nil
case BoolTy: case BoolTy:
return readBool(returnOutput) return readBool(returnOutput)
case AddressTy: case AddressTy:

View file

@ -117,6 +117,11 @@ var unpackTests = []unpackTest{
enc: "0000000000000000000000000000000000000000000000000000000000000001", enc: "0000000000000000000000000000000000000000000000000000000000000001",
want: big.NewInt(1), want: big.NewInt(1),
}, },
{
def: `[{"type": "int256"}]`,
enc: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
want: big.NewInt(-1),
},
{ {
def: `[{"type": "address"}]`, def: `[{"type": "address"}]`,
enc: "0000000000000000000000000100000000000000000000000000000000000000", enc: "0000000000000000000000000100000000000000000000000000000000000000",

View file

@ -179,26 +179,34 @@ func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Accou
return key, a, err return key, a, err
} }
func writeKeyFile(file string, content []byte) error { func writeTemporaryKeyFile(file string, content []byte) (string, error) {
// Create the keystore directory with appropriate permissions // Create the keystore directory with appropriate permissions
// in case it is not present yet. // in case it is not present yet.
const dirPerm = 0700 const dirPerm = 0700
if err := os.MkdirAll(filepath.Dir(file), dirPerm); err != nil { if err := os.MkdirAll(filepath.Dir(file), dirPerm); err != nil {
return err return "", err
} }
// Atomic write: create a temporary hidden file first // Atomic write: create a temporary hidden file first
// then move it into place. TempFile assigns mode 0600. // then move it into place. TempFile assigns mode 0600.
f, err := ioutil.TempFile(filepath.Dir(file), "."+filepath.Base(file)+".tmp") f, err := ioutil.TempFile(filepath.Dir(file), "."+filepath.Base(file)+".tmp")
if err != nil { if err != nil {
return err return "", err
} }
if _, err := f.Write(content); err != nil { if _, err := f.Write(content); err != nil {
f.Close() f.Close()
os.Remove(f.Name()) os.Remove(f.Name())
return err return "", err
} }
f.Close() f.Close()
return os.Rename(f.Name(), file) return f.Name(), nil
}
func writeKeyFile(file string, content []byte) error {
name, err := writeTemporaryKeyFile(file, content)
if err != nil {
return err
}
return os.Rename(name, file)
} }
// keyFileName implements the naming convention for keyfiles: // keyFileName implements the naming convention for keyfiles:

View file

@ -78,7 +78,7 @@ type unlocked struct {
// NewKeyStore creates a keystore for the given directory. // NewKeyStore creates a keystore for the given directory.
func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore { func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore {
keydir, _ = filepath.Abs(keydir) keydir, _ = filepath.Abs(keydir)
ks := &KeyStore{storage: &keyStorePassphrase{keydir, scryptN, scryptP}} ks := &KeyStore{storage: &keyStorePassphrase{keydir, scryptN, scryptP, false}}
ks.init(keydir) ks.init(keydir)
return ks return ks
} }

View file

@ -35,6 +35,7 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"os"
"path/filepath" "path/filepath"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -72,6 +73,10 @@ type keyStorePassphrase struct {
keysDirPath string keysDirPath string
scryptN int scryptN int
scryptP int scryptP int
// skipKeyFileVerification disables the security-feature which does
// reads and decrypts any newly created keyfiles. This should be 'false' in all
// cases except tests -- setting this to 'true' is not recommended.
skipKeyFileVerification bool
} }
func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, error) { func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, error) {
@ -93,7 +98,7 @@ func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string)
// StoreKey generates a key, encrypts with 'auth' and stores in the given directory // StoreKey generates a key, encrypts with 'auth' and stores in the given directory
func StoreKey(dir, auth string, scryptN, scryptP int) (common.Address, error) { func StoreKey(dir, auth string, scryptN, scryptP int) (common.Address, error) {
_, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP}, rand.Reader, auth) _, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP, false}, rand.Reader, auth)
return a.Address, err return a.Address, err
} }
@ -102,7 +107,25 @@ func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) er
if err != nil { if err != nil {
return err return err
} }
return writeKeyFile(filename, keyjson) // Write into temporary file
tmpName, err := writeTemporaryKeyFile(filename, keyjson)
if err != nil {
return err
}
if !ks.skipKeyFileVerification {
// Verify that we can decrypt the file with the given password.
_, err = ks.GetKey(key.Address, tmpName, auth)
if err != nil {
msg := "An error was encountered when saving and verifying the keystore file. \n" +
"This indicates that the keystore is corrupted. \n" +
"The corrupted file is stored at \n%v\n" +
"Please file a ticket at:\n\n" +
"https://github.com/ethereum/go-ethereum/issues." +
"The error was : %s"
return fmt.Errorf(msg, tmpName, err)
}
}
return os.Rename(tmpName, filename)
} }
func (ks keyStorePassphrase) JoinPath(filename string) string { func (ks keyStorePassphrase) JoinPath(filename string) string {

View file

@ -37,7 +37,7 @@ func tmpKeyStoreIface(t *testing.T, encrypted bool) (dir string, ks keyStore) {
t.Fatal(err) t.Fatal(err)
} }
if encrypted { if encrypted {
ks = &keyStorePassphrase{d, veryLightScryptN, veryLightScryptP} ks = &keyStorePassphrase{d, veryLightScryptN, veryLightScryptP, true}
} else { } else {
ks = &keyStorePlain{d} ks = &keyStorePlain{d}
} }
@ -191,7 +191,7 @@ func TestV1_1(t *testing.T) {
func TestV1_2(t *testing.T) { func TestV1_2(t *testing.T) {
t.Parallel() t.Parallel()
ks := &keyStorePassphrase{"testdata/v1", LightScryptN, LightScryptP} ks := &keyStorePassphrase{"testdata/v1", LightScryptN, LightScryptP, true}
addr := common.HexToAddress("cb61d5a9c4896fb9658090b597ef0e7be6f7b67e") addr := common.HexToAddress("cb61d5a9c4896fb9658090b597ef0e7be6f7b67e")
file := "testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e" file := "testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e"
k, err := ks.GetKey(addr, file, "g") k, err := ks.GetKey(addr, file, "g")

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.10.3.windows-%GETH_ARCH%.zip - appveyor DownloadFile https://storage.googleapis.com/golang/go1.11.windows-%GETH_ARCH%.zip
- 7z x go1.10.3.windows-%GETH_ARCH%.zip -y -oC:\ > NUL - 7z x go1.11.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
- go version - go version
- gcc --version - gcc --version

View file

@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/discv5"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/p2p/netutil"
) )
@ -85,7 +86,7 @@ func main() {
} }
if *writeAddr { if *writeAddr {
fmt.Printf("%v\n", discover.PubkeyID(&nodeKey.PublicKey)) fmt.Printf("%v\n", enode.PubkeyToIDV4(&nodeKey.PublicKey))
os.Exit(0) os.Exit(0)
} }

View file

@ -1,6 +1,13 @@
### Changelog for external API ### Changelog for external API
#### 4.0.0
* The external `account_Ecrecover`-method was removed.
* The external `account_Import`-method was removed.
#### 3.0.0
* The external `account_List`-method was changed to not expose `url`, which contained info about the local filesystem. It now returns only a list of addresses.
#### 2.0.0 #### 2.0.0

View file

@ -48,7 +48,7 @@ import (
) )
// ExternalAPIVersion -- see extapi_changelog.md // ExternalAPIVersion -- see extapi_changelog.md
const ExternalAPIVersion = "2.0.0" const ExternalAPIVersion = "3.0.0"
// InternalAPIVersion -- see intapi_changelog.md // InternalAPIVersion -- see intapi_changelog.md
const InternalAPIVersion = "2.0.0" const InternalAPIVersion = "2.0.0"
@ -70,6 +70,10 @@ var (
Value: 4, Value: 4,
Usage: "log level to emit to the screen", Usage: "log level to emit to the screen",
} }
advancedMode = cli.BoolFlag{
Name: "advanced",
Usage: "If enabled, issues warnings instead of rejections for suspicious requests. Default off",
}
keystoreFlag = cli.StringFlag{ keystoreFlag = cli.StringFlag{
Name: "keystore", Name: "keystore",
Value: filepath.Join(node.DefaultDataDir(), "keystore"), Value: filepath.Join(node.DefaultDataDir(), "keystore"),
@ -191,6 +195,7 @@ func init() {
ruleFlag, ruleFlag,
stdiouiFlag, stdiouiFlag,
testFlag, testFlag,
advancedMode,
} }
app.Action = signer app.Action = signer
app.Commands = []cli.Command{initCommand, attestCommand, addCredentialCommand} app.Commands = []cli.Command{initCommand, attestCommand, addCredentialCommand}
@ -225,7 +230,7 @@ func initializeSecrets(c *cli.Context) error {
if _, err := os.Stat(location); err == nil { if _, err := os.Stat(location); err == nil {
return fmt.Errorf("file %v already exists, will not overwrite", location) return fmt.Errorf("file %v already exists, will not overwrite", location)
} }
err = ioutil.WriteFile(location, masterSeed, 0700) err = ioutil.WriteFile(location, masterSeed, 0400)
if err != nil { if err != nil {
return err return err
} }
@ -384,7 +389,8 @@ func signer(c *cli.Context) error {
c.String(keystoreFlag.Name), c.String(keystoreFlag.Name),
c.Bool(utils.NoUSBFlag.Name), c.Bool(utils.NoUSBFlag.Name),
ui, db, ui, db,
c.Bool(utils.LightKDFFlag.Name)) c.Bool(utils.LightKDFFlag.Name),
c.Bool(advancedMode.Name))
api = apiImpl api = apiImpl
@ -540,14 +546,14 @@ func readMasterKey(ctx *cli.Context) ([]byte, error) {
// checkFile is a convenience function to check if a file // checkFile is a convenience function to check if a file
// * exists // * exists
// * is mode 0600 // * is mode 0400
func checkFile(filename string) error { func checkFile(filename string) error {
info, err := os.Stat(filename) info, err := os.Stat(filename)
if err != nil { if err != nil {
return fmt.Errorf("failed stat on %s: %v", filename, err) return fmt.Errorf("failed stat on %s: %v", filename, err)
} }
// Check the unix permission bits // Check the unix permission bits
if info.Mode().Perm()&077 != 0 { if info.Mode().Perm()&0377 != 0 {
return fmt.Errorf("file (%v) has insecure file permissions (%v)", filename, info.Mode().String()) return fmt.Errorf("file (%v) has insecure file permissions (%v)", filename, info.Mode().String())
} }
return nil return nil

View file

@ -52,7 +52,7 @@ INFO [02-21|12:14:38] Ruleset attestation updated sha256=6c21d17374
At this point, we then start the signer with the rule-file: At this point, we then start the signer with the rule-file:
```text ```text
#./signer --rules rules.json #./signer --rules rules.js
INFO [02-21|12:15:18] Using CLI as UI-channel INFO [02-21|12:15:18] Using CLI as UI-channel
INFO [02-21|12:15:18] Loaded 4byte db signatures=5509 file=./4byte.json INFO [02-21|12:15:18] Loaded 4byte db signatures=5509 file=./4byte.json
@ -153,7 +153,7 @@ INFO [02-21|14:36:30] Ruleset attestation updated sha256=2a0cb661da
And start the signer: And start the signer:
``` ```
#./signer --rules rules.js #./signer --rules rules.js --rpc
INFO [02-21|14:41:56] Using CLI as UI-channel INFO [02-21|14:41:56] Using CLI as UI-channel
INFO [02-21|14:41:56] Loaded 4byte db signatures=5509 file=./4byte.json INFO [02-21|14:41:56] Loaded 4byte db signatures=5509 file=./4byte.json
@ -190,7 +190,7 @@ INFO [02-21|14:42:56] Op rejected
The signer also stores all traffic over the external API in a log file. The last 4 lines shows the two requests and their responses: The signer also stores all traffic over the external API in a log file. The last 4 lines shows the two requests and their responses:
```text ```text
#tail audit.log -n 4 #tail -n 4 audit.log
t=2018-02-21T14:42:41+0100 lvl=info msg=Sign api=signer type=request metadata="{\"remote\":\"127.0.0.1:49706\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\"}" addr="0x694267f14675d7e1b9494fd8d72fefe1755710fa [chksum INVALID]" data=202062617a6f6e6b2062617a2067617a0a t=2018-02-21T14:42:41+0100 lvl=info msg=Sign api=signer type=request metadata="{\"remote\":\"127.0.0.1:49706\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\"}" addr="0x694267f14675d7e1b9494fd8d72fefe1755710fa [chksum INVALID]" data=202062617a6f6e6b2062617a2067617a0a
t=2018-02-21T14:42:42+0100 lvl=info msg=Sign api=signer type=response data=93e6161840c3ae1efc26dc68dedab6e8fc233bb3fefa1b4645dbf6609b93dace160572ea4ab33240256bb6d3dadb60dcd9c515d6374d3cf614ee897408d41d541c error=nil t=2018-02-21T14:42:42+0100 lvl=info msg=Sign api=signer type=response data=93e6161840c3ae1efc26dc68dedab6e8fc233bb3fefa1b4645dbf6609b93dace160572ea4ab33240256bb6d3dadb60dcd9c515d6374d3cf614ee897408d41d541c error=nil
t=2018-02-21T14:42:56+0100 lvl=info msg=Sign api=signer type=request metadata="{\"remote\":\"127.0.0.1:49708\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\"}" addr="0x694267f14675d7e1b9494fd8d72fefe1755710fa [chksum INVALID]" data=2020626f6e6b2062617a2067617a0a t=2018-02-21T14:42:56+0100 lvl=info msg=Sign api=signer type=request metadata="{\"remote\":\"127.0.0.1:49708\",\"local\":\"localhost:8550\",\"scheme\":\"HTTP/1.1\"}" addr="0x694267f14675d7e1b9494fd8d72fefe1755710fa [chksum INVALID]" data=2020626f6e6b2062617a2067617a0a

View file

@ -21,21 +21,33 @@ Private key information can be printed by using the `--private` flag;
make sure to use this feature with great caution! make sure to use this feature with great caution!
### `ethkey sign <keyfile> <message/file>` ### `ethkey signmessage <keyfile> <message/file>`
Sign the message with a keyfile. Sign the message with a keyfile.
It is possible to refer to a file containing the message. It is possible to refer to a file containing the message.
To sign a message contained in a file, use the `--msgfile` flag.
### `ethkey verify <address> <signature> <message/file>` ### `ethkey verifymessage <address> <signature> <message/file>`
Verify the signature of the message. Verify the signature of the message.
It is possible to refer to a file containing the message. It is possible to refer to a file containing the message.
To sign a message contained in a file, use the --msgfile flag.
### `ethkey changepassphrase <keyfile>`
Change the passphrase of a keyfile.
use the `--newpasswordfile` to point to the new password file.
## Passphrases ## Passphrases
For every command that uses a keyfile, you will be prompted to provide the For every command that uses a keyfile, you will be prompted to provide the
passphrase for decrypting the keyfile. To avoid this message, it is possible passphrase for decrypting the keyfile. To avoid this message, it is possible
to pass the passphrase by using the `--passphrase` flag pointing to a file that to pass the passphrase by using the `--passwordfile` flag pointing to a file that
contains the passphrase. contains the passphrase.
## JSON
In case you need to output the result in a JSON format, you shall by using the `--json` flag.

View file

@ -44,7 +44,7 @@ func disasmCmd(ctx *cli.Context) error {
return err return err
} }
code := strings.TrimSpace(string(in[:])) code := strings.TrimSpace(string(in))
fmt.Printf("%v\n", code) fmt.Printf("%v\n", code)
return asm.PrintDisassembled(code) return asm.PrintDisassembled(code)
} }

View file

@ -86,7 +86,7 @@ func runCmd(ctx *cli.Context) error {
chainConfig *params.ChainConfig chainConfig *params.ChainConfig
sender = common.BytesToAddress([]byte("sender")) sender = common.BytesToAddress([]byte("sender"))
receiver = common.BytesToAddress([]byte("receiver")) receiver = common.BytesToAddress([]byte("receiver"))
blockNumber uint64 genesisConfig *core.Genesis
) )
if ctx.GlobalBool(MachineFlag.Name) { if ctx.GlobalBool(MachineFlag.Name) {
tracer = NewJSONLogger(logconfig, os.Stdout) tracer = NewJSONLogger(logconfig, os.Stdout)
@ -98,13 +98,14 @@ func runCmd(ctx *cli.Context) error {
} }
if ctx.GlobalString(GenesisFlag.Name) != "" { if ctx.GlobalString(GenesisFlag.Name) != "" {
gen := readGenesis(ctx.GlobalString(GenesisFlag.Name)) gen := readGenesis(ctx.GlobalString(GenesisFlag.Name))
genesisConfig = gen
db := ethdb.NewMemDatabase() db := ethdb.NewMemDatabase()
genesis := gen.ToBlock(db) genesis := gen.ToBlock(db)
statedb, _ = state.New(genesis.Root(), state.NewDatabase(db)) statedb, _ = state.New(genesis.Root(), state.NewDatabase(db))
chainConfig = gen.Config chainConfig = gen.Config
blockNumber = gen.Number
} else { } else {
statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) statedb, _ = state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase()))
genesisConfig = new(core.Genesis)
} }
if ctx.GlobalString(SenderFlag.Name) != "" { if ctx.GlobalString(SenderFlag.Name) != "" {
sender = common.HexToAddress(ctx.GlobalString(SenderFlag.Name)) sender = common.HexToAddress(ctx.GlobalString(SenderFlag.Name))
@ -156,13 +157,19 @@ func runCmd(ctx *cli.Context) error {
} }
initialGas := ctx.GlobalUint64(GasFlag.Name) initialGas := ctx.GlobalUint64(GasFlag.Name)
if genesisConfig.GasLimit != 0 {
initialGas = genesisConfig.GasLimit
}
runtimeConfig := runtime.Config{ runtimeConfig := runtime.Config{
Origin: sender, Origin: sender,
State: statedb, State: statedb,
GasLimit: initialGas, GasLimit: initialGas,
GasPrice: utils.GlobalBig(ctx, PriceFlag.Name), GasPrice: utils.GlobalBig(ctx, PriceFlag.Name),
Value: utils.GlobalBig(ctx, ValueFlag.Name), Value: utils.GlobalBig(ctx, ValueFlag.Name),
BlockNumber: new(big.Int).SetUint64(blockNumber), Difficulty: genesisConfig.Difficulty,
Time: new(big.Int).SetUint64(genesisConfig.Timestamp),
Coinbase: genesisConfig.Coinbase,
BlockNumber: new(big.Int).SetUint64(genesisConfig.Number),
EVMConfig: vm.Config{ EVMConfig: vm.Config{
Tracer: tracer, Tracer: tracer,
Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name), Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),

View file

@ -54,8 +54,8 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/discv5"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"golang.org/x/net/websocket" "golang.org/x/net/websocket"
@ -157,7 +157,8 @@ func main() {
if blob, err = ioutil.ReadFile(*accPassFlag); err != nil { if blob, err = ioutil.ReadFile(*accPassFlag); err != nil {
log.Crit("Failed to read account password contents", "file", *accPassFlag, "err", err) log.Crit("Failed to read account password contents", "file", *accPassFlag, "err", err)
} }
pass := string(blob) // Delete trailing newline in password
pass := strings.TrimSuffix(string(blob), "\n")
ks := keystore.NewKeyStore(filepath.Join(os.Getenv("HOME"), ".faucet", "keys"), keystore.StandardScryptN, keystore.StandardScryptP) ks := keystore.NewKeyStore(filepath.Join(os.Getenv("HOME"), ".faucet", "keys"), keystore.StandardScryptN, keystore.StandardScryptP)
if blob, err = ioutil.ReadFile(*accJSONFlag); err != nil { if blob, err = ioutil.ReadFile(*accJSONFlag); err != nil {
@ -198,6 +199,8 @@ type faucet struct {
keystore *keystore.KeyStore // Keystore containing the single signer keystore *keystore.KeyStore // Keystore containing the single signer
account accounts.Account // Account funding user faucet requests account accounts.Account // Account funding user faucet requests
head *types.Header // Current head header of the faucet
balance *big.Int // Current balance of the faucet
nonce uint64 // Current pending nonce of the faucet nonce uint64 // Current pending nonce of the faucet
price *big.Int // Current gas price to issue funds with price *big.Int // Current gas price to issue funds with
@ -252,9 +255,11 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network u
return nil, err return nil, err
} }
for _, boot := range enodes { for _, boot := range enodes {
old, _ := discover.ParseNode(boot.String()) old, err := enode.ParseV4(boot.String())
if err != nil {
stack.Server().AddPeer(old) stack.Server().AddPeer(old)
} }
}
// Attach to the client and retrieve and interesting metadatas // Attach to the client and retrieve and interesting metadatas
api, err := stack.Attach() api, err := stack.Attach()
if err != nil { if err != nil {
@ -323,33 +328,30 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
nonce uint64 nonce uint64
err error err error
) )
for { for head == nil || balance == nil {
// Attempt to retrieve the stats, may error on no faucet connectivity // Retrieve the current stats cached by the faucet
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) f.lock.RLock()
head, err = f.client.HeaderByNumber(ctx, nil) if f.head != nil {
if err == nil { head = types.CopyHeader(f.head)
balance, err = f.client.BalanceAt(ctx, f.account.Address, head.Number)
if err == nil {
nonce, err = f.client.NonceAt(ctx, f.account.Address, nil)
} }
if f.balance != nil {
balance = new(big.Int).Set(f.balance)
} }
cancel() nonce = f.nonce
f.lock.RUnlock()
// If stats retrieval failed, wait a bit and retry if head == nil || balance == nil {
if err != nil { // Report the faucet offline until initial stats are ready
if err = sendError(conn, errors.New("Faucet offline: "+err.Error())); err != nil { if err = sendError(conn, errors.New("Faucet offline")); err != nil {
log.Warn("Failed to send faucet error to client", "err", err) log.Warn("Failed to send faucet error to client", "err", err)
return return
} }
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)
continue
} }
// Initial stats reported successfully, proceed with user interaction
break
} }
// Send over the initial stats and the latest header // Send over the initial stats and the latest header
if err = send(conn, map[string]interface{}{ if err = send(conn, map[string]interface{}{
"funds": balance.Div(balance, ether), "funds": new(big.Int).Div(balance, ether),
"funded": nonce, "funded": nonce,
"peers": f.stack.Server().PeerCount(), "peers": f.stack.Server().PeerCount(),
"requests": f.reqs, "requests": f.reqs,
@ -519,6 +521,47 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
} }
} }
// refresh attempts to retrieve the latest header from the chain and extract the
// associated faucet balance and nonce for connectivity caching.
func (f *faucet) refresh(head *types.Header) error {
// Ensure a state update does not run for too long
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// If no header was specified, use the current chain head
var err error
if head == nil {
if head, err = f.client.HeaderByNumber(ctx, nil); err != nil {
return err
}
}
// Retrieve the balance, nonce and gas price from the current head
var (
balance *big.Int
nonce uint64
price *big.Int
)
if balance, err = f.client.BalanceAt(ctx, f.account.Address, head.Number); err != nil {
return err
}
if nonce, err = f.client.NonceAt(ctx, f.account.Address, head.Number); err != nil {
return err
}
if price, err = f.client.SuggestGasPrice(ctx); err != nil {
return err
}
// Everything succeeded, update the cached stats and eject old requests
f.lock.Lock()
f.head, f.balance = head, balance
f.price, f.nonce = price, nonce
for len(f.reqs) > 0 && f.reqs[0].Tx.Nonce() < f.nonce {
f.reqs = f.reqs[1:]
}
f.lock.Unlock()
return nil
}
// loop keeps waiting for interesting events and pushes them out to connected // loop keeps waiting for interesting events and pushes them out to connected
// websockets. // websockets.
func (f *faucet) loop() { func (f *faucet) loop() {
@ -536,45 +579,27 @@ func (f *faucet) loop() {
go func() { go func() {
for head := range update { for head := range update {
// New chain head arrived, query the current stats and stream to clients // New chain head arrived, query the current stats and stream to clients
var ( timestamp := time.Unix(head.Time.Int64(), 0)
balance *big.Int if time.Since(timestamp) > time.Hour {
nonce uint64 log.Warn("Skipping faucet refresh, head too old", "number", head.Number, "hash", head.Hash(), "age", common.PrettyAge(timestamp))
price *big.Int continue
err error
)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
balance, err = f.client.BalanceAt(ctx, f.account.Address, head.Number)
if err == nil {
nonce, err = f.client.NonceAt(ctx, f.account.Address, nil)
if err == nil {
price, err = f.client.SuggestGasPrice(ctx)
} }
} if err := f.refresh(head); err != nil {
cancel()
// If querying the data failed, try for the next block
if err != nil {
log.Warn("Failed to update faucet state", "block", head.Number, "hash", head.Hash(), "err", err) log.Warn("Failed to update faucet state", "block", head.Number, "hash", head.Hash(), "err", err)
continue continue
} else {
log.Info("Updated faucet state", "block", head.Number, "hash", head.Hash(), "balance", balance, "nonce", nonce, "price", price)
} }
// Faucet state retrieved, update locally and send to clients // Faucet state retrieved, update locally and send to clients
balance = new(big.Int).Div(balance, ether)
f.lock.Lock()
f.price, f.nonce = price, nonce
for len(f.reqs) > 0 && f.reqs[0].Tx.Nonce() < f.nonce {
f.reqs = f.reqs[1:]
}
f.lock.Unlock()
f.lock.RLock() f.lock.RLock()
log.Info("Updated faucet state", "number", head.Number, "hash", head.Hash(), "age", common.PrettyAge(timestamp), "balance", f.balance, "nonce", f.nonce, "price", f.price)
balance := new(big.Int).Div(f.balance, ether)
peers := f.stack.Server().PeerCount()
for _, conn := range f.conns { for _, conn := range f.conns {
if err := send(conn, map[string]interface{}{ if err := send(conn, map[string]interface{}{
"funds": balance, "funds": balance,
"funded": f.nonce, "funded": f.nonce,
"peers": f.stack.Server().PeerCount(), "peers": peers,
"requests": f.reqs, "requests": f.reqs,
}, time.Second); err != nil { }, time.Second); err != nil {
log.Warn("Failed to send stats to client", "err", err) log.Warn("Failed to send stats to client", "err", err)

View file

@ -340,9 +340,9 @@ func importPreimages(ctx *cli.Context) error {
start := time.Now() start := time.Now()
if err := utils.ImportPreimages(diskdb, ctx.Args().First()); err != nil { if err := utils.ImportPreimages(diskdb, ctx.Args().First()); err != nil {
utils.Fatalf("Export error: %v\n", err) utils.Fatalf("Import error: %v\n", err)
} }
fmt.Printf("Export done in %v\n", time.Since(start)) fmt.Printf("Import done in %v\n", time.Since(start))
return nil return nil
} }

View file

@ -168,6 +168,9 @@ func makeFullNode(ctx *cli.Context) *node.Node {
if ctx.GlobalIsSet(utils.WhisperMinPOWFlag.Name) { if ctx.GlobalIsSet(utils.WhisperMinPOWFlag.Name) {
cfg.Shh.MinimumAcceptedPOW = ctx.Float64(utils.WhisperMinPOWFlag.Name) cfg.Shh.MinimumAcceptedPOW = ctx.Float64(utils.WhisperMinPOWFlag.Name)
} }
if ctx.GlobalIsSet(utils.WhisperRestrictConnectionBetweenLightClientsFlag.Name) {
cfg.Shh.RestrictConnectionBetweenLightClients = true
}
utils.RegisterShhService(stack, &cfg.Shh) utils.RegisterShhService(stack, &cfg.Shh)
} }

View file

@ -132,6 +132,8 @@ var (
utils.NoCompactionFlag, utils.NoCompactionFlag,
utils.GpoBlocksFlag, utils.GpoBlocksFlag,
utils.GpoPercentileFlag, utils.GpoPercentileFlag,
utils.EWASMInterpreterFlag,
utils.EVMInterpreterFlag,
configFileFlag, configFileFlag,
} }
@ -153,6 +155,7 @@ var (
utils.WhisperEnabledFlag, utils.WhisperEnabledFlag,
utils.WhisperMaxMessageSizeFlag, utils.WhisperMaxMessageSizeFlag,
utils.WhisperMinPOWFlag, utils.WhisperMinPOWFlag,
utils.WhisperRestrictConnectionBetweenLightClientsFlag,
} }
metricsFlags = []cli.Flag{ metricsFlags = []cli.Flag{

View file

@ -208,6 +208,8 @@ var AppHelpFlagGroups = []flagGroup{
Name: "VIRTUAL MACHINE", Name: "VIRTUAL MACHINE",
Flags: []cli.Flag{ Flags: []cli.Flag{
utils.VMEnableDebugFlag, utils.VMEnableDebugFlag,
utils.EVMInterpreterFlag,
utils.EWASMInterpreterFlag,
}, },
}, },
{ {

View file

@ -47,7 +47,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/simulations" "github.com/ethereum/go-ethereum/p2p/simulations"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters" "github.com/ethereum/go-ethereum/p2p/simulations/adapters"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
@ -285,7 +285,7 @@ func createNode(ctx *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
config.ID = discover.PubkeyID(&privKey.PublicKey) config.ID = enode.PubkeyToIDV4(&privKey.PublicKey)
config.PrivateKey = privKey config.PrivateKey = privKey
} }
if services := ctx.String("services"); services != "" { if services := ctx.String("services"); services != "" {

View file

@ -92,7 +92,7 @@ func (w *wizard) deployDashboard() {
pages = append(pages, page) pages = append(pages, page)
} }
} }
// Promt the user to chose one, enter manually or simply not list this service // Prompt the user to chose one, enter manually or simply not list this service
defLabel, defChoice := "don't list", len(pages)+2 defLabel, defChoice := "don't list", len(pages)+2
if len(pages) > 0 { if len(pages) > 0 {
defLabel, defChoice = pages[0], 1 defLabel, defChoice = pages[0], 1

View file

@ -87,7 +87,7 @@ func (w *wizard) makeServer() string {
return input return input
} }
// selectServer lists the user all the currnetly known servers to choose from, // selectServer lists the user all the currently known servers to choose from,
// also granting the option to add a new one. // also granting the option to add a new one.
func (w *wizard) selectServer() string { func (w *wizard) selectServer() string {
// List the available server to the user and wait for a choice // List the available server to the user and wait for a choice
@ -115,7 +115,7 @@ func (w *wizard) selectServer() string {
// manageComponents displays a list of network components the user can tear down // manageComponents displays a list of network components the user can tear down
// and an option // and an option
func (w *wizard) manageComponents() { func (w *wizard) manageComponents() {
// List all the componens we can tear down, along with an entry to deploy a new one // List all the components we can tear down, along with an entry to deploy a new one
fmt.Println() fmt.Println()
var serviceHosts, serviceNames []string var serviceHosts, serviceNames []string

View file

@ -51,7 +51,7 @@ func accessNewPass(ctx *cli.Context) {
password = getPassPhrase("", 0, makePasswordList(ctx)) password = getPassPhrase("", 0, makePasswordList(ctx))
dryRun = ctx.Bool(SwarmDryRunFlag.Name) dryRun = ctx.Bool(SwarmDryRunFlag.Name)
) )
accessKey, ae, err = api.DoPasswordNew(ctx, password, salt) accessKey, ae, err = api.DoPassword(ctx, password, salt)
if err != nil { if err != nil {
utils.Fatalf("error getting session key: %v", err) utils.Fatalf("error getting session key: %v", err)
} }
@ -62,7 +62,6 @@ func accessNewPass(ctx *cli.Context) {
utils.Fatalf("had an error printing the manifests: %v", err) utils.Fatalf("had an error printing the manifests: %v", err)
} }
} else { } else {
utils.Fatalf("uploading manifests")
err = uploadManifests(ctx, m, nil) err = uploadManifests(ctx, m, nil)
if err != nil { if err != nil {
utils.Fatalf("had an error uploading the manifests: %v", err) utils.Fatalf("had an error uploading the manifests: %v", err)
@ -85,7 +84,7 @@ func accessNewPK(ctx *cli.Context) {
granteePublicKey = ctx.String(SwarmAccessGrantKeyFlag.Name) granteePublicKey = ctx.String(SwarmAccessGrantKeyFlag.Name)
dryRun = ctx.Bool(SwarmDryRunFlag.Name) dryRun = ctx.Bool(SwarmDryRunFlag.Name)
) )
sessionKey, ae, err = api.DoPKNew(ctx, privateKey, granteePublicKey, salt) sessionKey, ae, err = api.DoPK(ctx, privateKey, granteePublicKey, salt)
if err != nil { if err != nil {
utils.Fatalf("error getting session key: %v", err) utils.Fatalf("error getting session key: %v", err)
} }
@ -115,18 +114,33 @@ func accessNewACT(ctx *cli.Context) {
accessKey []byte accessKey []byte
err error err error
ref = args[0] ref = args[0]
grantees = []string{} pkGrantees = []string{}
actFilename = ctx.String(SwarmAccessGrantKeysFlag.Name) passGrantees = []string{}
pkGranteesFilename = ctx.String(SwarmAccessGrantKeysFlag.Name)
passGranteesFilename = ctx.String(utils.PasswordFileFlag.Name)
privateKey = getPrivKey(ctx) privateKey = getPrivKey(ctx)
dryRun = ctx.Bool(SwarmDryRunFlag.Name) dryRun = ctx.Bool(SwarmDryRunFlag.Name)
) )
if pkGranteesFilename == "" && passGranteesFilename == "" {
utils.Fatalf("you have to provide either a grantee public-keys file or an encryption passwords file (or both)")
}
bytes, err := ioutil.ReadFile(actFilename) if pkGranteesFilename != "" {
bytes, err := ioutil.ReadFile(pkGranteesFilename)
if err != nil { if err != nil {
utils.Fatalf("had an error reading the grantee public key list") utils.Fatalf("had an error reading the grantee public key list")
} }
grantees = strings.Split(string(bytes), "\n") pkGrantees = strings.Split(string(bytes), "\n")
accessKey, ae, actManifest, err = api.DoACTNew(ctx, privateKey, salt, grantees) }
if passGranteesFilename != "" {
bytes, err := ioutil.ReadFile(passGranteesFilename)
if err != nil {
utils.Fatalf("could not read password filename: %v", err)
}
passGrantees = strings.Split(string(bytes), "\n")
}
accessKey, ae, actManifest, err = api.DoACT(ctx, privateKey, salt, pkGrantees, passGrantees)
if err != nil { if err != nil {
utils.Fatalf("error generating ACT manifest: %v", err) utils.Fatalf("error generating ACT manifest: %v", err)
} }

View file

@ -28,18 +28,26 @@ import (
gorand "math/rand" gorand "math/rand"
"net/http" "net/http"
"os" "os"
"path/filepath"
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/ecies"
"github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/swarm/api" "github.com/ethereum/go-ethereum/swarm/api"
swarm "github.com/ethereum/go-ethereum/swarm/api/client" swarm "github.com/ethereum/go-ethereum/swarm/api/client"
"github.com/ethereum/go-ethereum/swarm/testutil"
) )
const (
hashRegexp = `[a-f\d]{128}`
data = "notsorandomdata"
)
var DefaultCurve = crypto.S256()
// TestAccessPassword tests for the correct creation of an ACT manifest protected by a password. // TestAccessPassword tests for the correct creation of an ACT manifest protected by a password.
// The test creates bogus content, uploads it encrypted, then creates the wrapping manifest with the Access entry // The test creates bogus content, uploads it encrypted, then creates the wrapping manifest with the Access entry
// The parties participating - node (publisher), uploads to second node then disappears. Content which was uploaded // The parties participating - node (publisher), uploads to second node then disappears. Content which was uploaded
@ -50,23 +58,8 @@ func TestAccessPassword(t *testing.T) {
defer cluster.Shutdown() defer cluster.Shutdown()
proxyNode := cluster.Nodes[0] proxyNode := cluster.Nodes[0]
// create a tmp file dataFilename := testutil.TempFileWithContent(t, data)
tmp, err := ioutil.TempDir("", "swarm-test") defer os.RemoveAll(dataFilename)
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmp)
// write data to file
data := "notsorandomdata"
dataFilename := filepath.Join(tmp, "data.txt")
err = ioutil.WriteFile(dataFilename, []byte(data), 0666)
if err != nil {
t.Fatal(err)
}
hashRegexp := `[a-f\d]{128}`
// upload the file with 'swarm up' and expect a hash // upload the file with 'swarm up' and expect a hash
up := runSwarm(t, up := runSwarm(t,
@ -83,14 +76,14 @@ func TestAccessPassword(t *testing.T) {
} }
ref := matches[0] ref := matches[0]
tmp, err := ioutil.TempDir("", "swarm-test")
password := "smth"
passwordFilename := filepath.Join(tmp, "password.txt")
err = ioutil.WriteFile(passwordFilename, []byte(password), 0666)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer os.RemoveAll(tmp)
password := "smth"
passwordFilename := testutil.TempFileWithContent(t, "smth")
defer os.RemoveAll(passwordFilename)
up = runSwarm(t, up = runSwarm(t,
"access", "access",
@ -142,7 +135,9 @@ func TestAccessPassword(t *testing.T) {
if a.KdfParams == nil { if a.KdfParams == nil {
t.Fatal("manifest access kdf params is nil") t.Fatal("manifest access kdf params is nil")
} }
if a.Publisher != "" {
t.Fatal("should be empty")
}
client := swarm.NewClient(cluster.Nodes[0].URL) client := swarm.NewClient(cluster.Nodes[0].URL)
hash, err := client.UploadManifest(&m, false) hash, err := client.UploadManifest(&m, false)
@ -188,12 +183,8 @@ func TestAccessPassword(t *testing.T) {
t.Errorf("expected decrypted data %q, got %q", data, string(d)) t.Errorf("expected decrypted data %q, got %q", data, string(d))
} }
wrongPasswordFilename := filepath.Join(tmp, "password-wrong.txt") wrongPasswordFilename := testutil.TempFileWithContent(t, "just wr0ng")
defer os.RemoveAll(wrongPasswordFilename)
err = ioutil.WriteFile(wrongPasswordFilename, []byte("just wr0ng"), 0666)
if err != nil {
t.Fatal(err)
}
//download file with 'swarm down' with wrong password //download file with 'swarm down' with wrong password
up = runSwarm(t, up = runSwarm(t,
@ -219,25 +210,11 @@ func TestAccessPassword(t *testing.T) {
// the test will fail if the proxy's given private key is not granted on the ACT. // the test will fail if the proxy's given private key is not granted on the ACT.
func TestAccessPK(t *testing.T) { func TestAccessPK(t *testing.T) {
// Setup Swarm and upload a test file to it // Setup Swarm and upload a test file to it
cluster := newTestCluster(t, 1) cluster := newTestCluster(t, 2)
defer cluster.Shutdown() defer cluster.Shutdown()
// create a tmp file dataFilename := testutil.TempFileWithContent(t, data)
tmp, err := ioutil.TempFile("", "swarm-test") defer os.RemoveAll(dataFilename)
if err != nil {
t.Fatal(err)
}
defer tmp.Close()
defer os.Remove(tmp.Name())
// write data to file
data := "notsorandomdata"
_, err = io.WriteString(tmp, data)
if err != nil {
t.Fatal(err)
}
hashRegexp := `[a-f\d]{128}`
// upload the file with 'swarm up' and expect a hash // upload the file with 'swarm up' and expect a hash
up := runSwarm(t, up := runSwarm(t,
@ -245,7 +222,7 @@ func TestAccessPK(t *testing.T) {
cluster.Nodes[0].URL, cluster.Nodes[0].URL,
"up", "up",
"--encrypt", "--encrypt",
tmp.Name()) dataFilename)
_, matches := up.ExpectRegexp(hashRegexp) _, matches := up.ExpectRegexp(hashRegexp)
up.ExpectExit() up.ExpectExit()
@ -254,7 +231,6 @@ func TestAccessPK(t *testing.T) {
} }
ref := matches[0] ref := matches[0]
pk := cluster.Nodes[0].PrivateKey pk := cluster.Nodes[0].PrivateKey
granteePubKey := crypto.CompressPubkey(&pk.PublicKey) granteePubKey := crypto.CompressPubkey(&pk.PublicKey)
@ -263,22 +239,15 @@ func TestAccessPK(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
passFile, err := ioutil.TempFile("", "swarm-test") passwordFilename := testutil.TempFileWithContent(t, testPassphrase)
if err != nil { defer os.RemoveAll(passwordFilename)
t.Fatal(err)
}
defer passFile.Close()
defer os.Remove(passFile.Name())
_, err = io.WriteString(passFile, testPassphrase)
if err != nil {
t.Fatal(err)
}
_, publisherAccount := getTestAccount(t, publisherDir) _, publisherAccount := getTestAccount(t, publisherDir)
up = runSwarm(t, up = runSwarm(t,
"--bzzaccount", "--bzzaccount",
publisherAccount.Address.String(), publisherAccount.Address.String(),
"--password", "--password",
passFile.Name(), passwordFilename,
"--datadir", "--datadir",
publisherDir, publisherDir,
"--bzzapi", "--bzzapi",
@ -299,6 +268,20 @@ func TestAccessPK(t *testing.T) {
t.Fatalf("stdout not matched") t.Fatalf("stdout not matched")
} }
//get the public key from the publisher directory
publicKeyFromDataDir := runSwarm(t,
"--bzzaccount",
publisherAccount.Address.String(),
"--password",
passwordFilename,
"--datadir",
publisherDir,
"print-keys",
"--compressed",
)
_, publicKeyString := publicKeyFromDataDir.ExpectRegexp(".+")
publicKeyFromDataDir.ExpectExit()
pkComp := strings.Split(publicKeyString[0], "=")[1]
var m api.Manifest var m api.Manifest
err = json.Unmarshal([]byte(matches[0]), &m) err = json.Unmarshal([]byte(matches[0]), &m)
@ -332,7 +315,9 @@ func TestAccessPK(t *testing.T) {
if a.KdfParams != nil { if a.KdfParams != nil {
t.Fatal("manifest access kdf params should be nil") t.Fatal("manifest access kdf params should be nil")
} }
if a.Publisher != pkComp {
t.Fatal("publisher key did not match")
}
client := swarm.NewClient(cluster.Nodes[0].URL) client := swarm.NewClient(cluster.Nodes[0].URL)
hash, err := client.UploadManifest(&m, false) hash, err := client.UploadManifest(&m, false)
@ -359,36 +344,34 @@ func TestAccessPK(t *testing.T) {
} }
} }
// TestAccessACT tests the e2e creation, uploading and downloading of an ACT type access control // TestAccessACT tests the creation of the ACT manifest end-to-end, without any bogus entries (i.e. default scenario = 3 nodes 1 unauthorized)
// the test fires up a 3 node cluster, then randomly picks 2 nodes which will be acting as grantees to the data
// set. the third node should fail decoding the reference as it will not be granted access. the publisher uploads through
// one of the nodes then disappears.
func TestAccessACT(t *testing.T) { func TestAccessACT(t *testing.T) {
testAccessACT(t, 0)
}
// TestAccessACTScale tests the creation of the ACT manifest end-to-end, with 1000 bogus entries (i.e. 1000 EC keys + default scenario = 3 nodes 1 unauthorized = 1003 keys in the ACT manifest)
func TestAccessACTScale(t *testing.T) {
testAccessACT(t, 1000)
}
// TestAccessACT tests the e2e creation, uploading and downloading of an ACT access control with both EC keys AND password protection
// the test fires up a 3 node cluster, then randomly picks 2 nodes which will be acting as grantees to the data
// set and also protects the ACT with a password. the third node should fail decoding the reference as it will not be granted access.
// the third node then then tries to download using a correct password (and succeeds) then uses a wrong password and fails.
// the publisher uploads through one of the nodes then disappears.
func testAccessACT(t *testing.T, bogusEntries int) {
// Setup Swarm and upload a test file to it // Setup Swarm and upload a test file to it
cluster := newTestCluster(t, 3) const clusterSize = 3
cluster := newTestCluster(t, clusterSize)
defer cluster.Shutdown() defer cluster.Shutdown()
var uploadThroughNode = cluster.Nodes[0] var uploadThroughNode = cluster.Nodes[0]
client := swarm.NewClient(uploadThroughNode.URL) client := swarm.NewClient(uploadThroughNode.URL)
r1 := gorand.New(gorand.NewSource(time.Now().UnixNano())) r1 := gorand.New(gorand.NewSource(time.Now().UnixNano()))
nodeToSkip := r1.Intn(3) // a number between 0 and 2 (node indices in `cluster`) nodeToSkip := r1.Intn(clusterSize) // a number between 0 and 2 (node indices in `cluster`)
// create a tmp file dataFilename := testutil.TempFileWithContent(t, data)
tmp, err := ioutil.TempFile("", "swarm-test") defer os.RemoveAll(dataFilename)
if err != nil {
t.Fatal(err)
}
defer tmp.Close()
defer os.Remove(tmp.Name())
// write data to file
data := "notsorandomdata"
_, err = io.WriteString(tmp, data)
if err != nil {
t.Fatal(err)
}
hashRegexp := `[a-f\d]{128}`
// upload the file with 'swarm up' and expect a hash // upload the file with 'swarm up' and expect a hash
up := runSwarm(t, up := runSwarm(t,
@ -396,7 +379,7 @@ func TestAccessACT(t *testing.T) {
cluster.Nodes[0].URL, cluster.Nodes[0].URL,
"up", "up",
"--encrypt", "--encrypt",
tmp.Name()) dataFilename)
_, matches := up.ExpectRegexp(hashRegexp) _, matches := up.ExpectRegexp(hashRegexp)
up.ExpectExit() up.ExpectExit()
@ -415,41 +398,42 @@ func TestAccessACT(t *testing.T) {
grantees = append(grantees, hex.EncodeToString(granteePubKey)) grantees = append(grantees, hex.EncodeToString(granteePubKey))
} }
granteesPubkeyListFile, err := ioutil.TempFile("", "grantees-pubkey-list.csv") if bogusEntries > 0 {
bogusGrantees := []string{}
for i := 0; i < bogusEntries; i++ {
prv, err := ecies.GenerateKey(rand.Reader, DefaultCurve, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
bogusGrantees = append(bogusGrantees, hex.EncodeToString(crypto.CompressPubkey(&prv.ExportECDSA().PublicKey)))
_, err = granteesPubkeyListFile.WriteString(strings.Join(grantees, "\n"))
if err != nil {
t.Fatal(err)
} }
r2 := gorand.New(gorand.NewSource(time.Now().UnixNano()))
defer granteesPubkeyListFile.Close() for i := 0; i < len(grantees); i++ {
defer os.Remove(granteesPubkeyListFile.Name()) insertAtIdx := r2.Intn(len(bogusGrantees))
bogusGrantees = append(bogusGrantees[:insertAtIdx], append([]string{grantees[i]}, bogusGrantees[insertAtIdx:]...)...)
}
grantees = bogusGrantees
}
granteesPubkeyListFile := testutil.TempFileWithContent(t, strings.Join(grantees, "\n"))
defer os.RemoveAll(granteesPubkeyListFile)
publisherDir, err := ioutil.TempDir("", "swarm-account-dir-temp") publisherDir, err := ioutil.TempDir("", "swarm-account-dir-temp")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer os.RemoveAll(publisherDir)
passFile, err := ioutil.TempFile("", "swarm-test") passwordFilename := testutil.TempFileWithContent(t, testPassphrase)
if err != nil { defer os.RemoveAll(passwordFilename)
t.Fatal(err) actPasswordFilename := testutil.TempFileWithContent(t, "smth")
} defer os.RemoveAll(actPasswordFilename)
defer passFile.Close()
defer os.Remove(passFile.Name())
_, err = io.WriteString(passFile, testPassphrase)
if err != nil {
t.Fatal(err)
}
_, publisherAccount := getTestAccount(t, publisherDir) _, publisherAccount := getTestAccount(t, publisherDir)
up = runSwarm(t, up = runSwarm(t,
"--bzzaccount", "--bzzaccount",
publisherAccount.Address.String(), publisherAccount.Address.String(),
"--password", "--password",
passFile.Name(), passwordFilename,
"--datadir", "--datadir",
publisherDir, publisherDir,
"--bzzapi", "--bzzapi",
@ -458,7 +442,9 @@ func TestAccessACT(t *testing.T) {
"new", "new",
"act", "act",
"--grant-keys", "--grant-keys",
granteesPubkeyListFile.Name(), granteesPubkeyListFile,
"--password",
actPasswordFilename,
ref, ref,
) )
@ -468,6 +454,22 @@ func TestAccessACT(t *testing.T) {
if len(matches) == 0 { if len(matches) == 0 {
t.Fatalf("stdout not matched") t.Fatalf("stdout not matched")
} }
//get the public key from the publisher directory
publicKeyFromDataDir := runSwarm(t,
"--bzzaccount",
publisherAccount.Address.String(),
"--password",
passwordFilename,
"--datadir",
publisherDir,
"print-keys",
"--compressed",
)
_, publicKeyString := publicKeyFromDataDir.ExpectRegexp(".+")
publicKeyFromDataDir.ExpectExit()
pkComp := strings.Split(publicKeyString[0], "=")[1]
hash := matches[0] hash := matches[0]
m, _, err := client.DownloadManifest(hash) m, _, err := client.DownloadManifest(hash)
if err != nil { if err != nil {
@ -497,10 +499,10 @@ func TestAccessACT(t *testing.T) {
if len(a.Salt) < 32 { if len(a.Salt) < 32 {
t.Fatalf(`got salt with length %v, expected not less the 32 bytes`, len(a.Salt)) t.Fatalf(`got salt with length %v, expected not less the 32 bytes`, len(a.Salt))
} }
if a.KdfParams != nil {
t.Fatal("manifest access kdf params should be nil")
}
if a.Publisher != pkComp {
t.Fatal("publisher key did not match")
}
httpClient := &http.Client{} httpClient := &http.Client{}
// all nodes except the skipped node should be able to decrypt the content // all nodes except the skipped node should be able to decrypt the content
@ -521,6 +523,25 @@ func TestAccessACT(t *testing.T) {
t.Fatalf("should be a 401") t.Fatalf("should be a 401")
} }
// try downloading using a password instead, using the unauthorized node
passwordUrl := strings.Replace(url, "http://", "http://:smth@", -1)
response, err = httpClient.Get(passwordUrl)
if err != nil {
t.Fatal(err)
}
if response.StatusCode != http.StatusOK {
t.Fatal("should be a 200")
}
// now try with the wrong password, expect 401
passwordUrl = strings.Replace(url, "http://", "http://:smthWrong@", -1)
response, err = httpClient.Get(passwordUrl)
if err != nil {
t.Fatal(err)
}
if response.StatusCode != http.StatusUnauthorized {
t.Fatal("should be a 401")
}
continue continue
} }

View file

@ -39,7 +39,7 @@ func hash(ctx *cli.Context) {
defer f.Close() defer f.Close()
stat, _ := f.Stat() stat, _ := f.Stat()
fileStore := storage.NewFileStore(storage.NewMapChunkStore(), storage.NewFileStoreParams()) fileStore := storage.NewFileStore(&storage.FakeChunkStore{}, storage.NewFileStoreParams())
addr, _, err := fileStore.Store(context.TODO(), f, stat.Size(), false) addr, _, err := fileStore.Store(context.TODO(), f, stat.Size(), false)
if err != nil { if err != nil {
utils.Fatalf("%v\n", err) utils.Fatalf("%v\n", err)

View file

@ -18,6 +18,7 @@ package main
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"encoding/hex"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
@ -37,7 +38,7 @@ import (
"github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/swarm" "github.com/ethereum/go-ethereum/swarm"
bzzapi "github.com/ethereum/go-ethereum/swarm/api" bzzapi "github.com/ethereum/go-ethereum/swarm/api"
swarmmetrics "github.com/ethereum/go-ethereum/swarm/metrics" swarmmetrics "github.com/ethereum/go-ethereum/swarm/metrics"
@ -208,6 +209,10 @@ var (
Name: "data", Name: "data",
Usage: "Initializes the resource with the given hex-encoded data. Data must be prefixed by 0x", Usage: "Initializes the resource with the given hex-encoded data. Data must be prefixed by 0x",
} }
SwarmCompressedFlag = cli.BoolFlag{
Name: "compressed",
Usage: "Prints encryption keys in compressed form",
}
) )
//declare a few constant error messages, useful for later error check comparisons in test //declare a few constant error messages, useful for later error check comparisons in test
@ -252,6 +257,14 @@ func init() {
Usage: "Print version numbers", Usage: "Print version numbers",
Description: "The output of this command is supposed to be machine-readable", Description: "The output of this command is supposed to be machine-readable",
}, },
{
Action: keys,
CustomHelpTemplate: helpTemplate,
Name: "print-keys",
Flags: []cli.Flag{SwarmCompressedFlag},
Usage: "Print public key information",
Description: "The output of this command is supposed to be machine-readable",
},
{ {
Action: upload, Action: upload,
CustomHelpTemplate: helpTemplate, CustomHelpTemplate: helpTemplate,
@ -306,6 +319,7 @@ func init() {
Flags: []cli.Flag{ Flags: []cli.Flag{
SwarmAccessGrantKeysFlag, SwarmAccessGrantKeysFlag,
SwarmDryRunFlag, SwarmDryRunFlag,
utils.PasswordFileFlag,
}, },
Name: "act", Name: "act",
Usage: "encrypts a reference with the node's private key and a given grantee's public key and embeds it into a root manifest", Usage: "encrypts a reference with the node's private key and a given grantee's public key and embeds it into a root manifest",
@ -580,6 +594,17 @@ func main() {
} }
} }
func keys(ctx *cli.Context) error {
privateKey := getPrivKey(ctx)
pub := hex.EncodeToString(crypto.FromECDSAPub(&privateKey.PublicKey))
pubCompressed := hex.EncodeToString(crypto.CompressPubkey(&privateKey.PublicKey))
if !ctx.Bool(SwarmCompressedFlag.Name) {
fmt.Println(fmt.Sprintf("publicKey=%s", pub))
}
fmt.Println(fmt.Sprintf("publicKeyCompressed=%s", pubCompressed))
return nil
}
func version(ctx *cli.Context) error { func version(ctx *cli.Context) error {
fmt.Println(strings.Title(clientIdentifier)) fmt.Println(strings.Title(clientIdentifier))
fmt.Println("Version:", sv.VersionWithMeta) fmt.Println("Version:", sv.VersionWithMeta)
@ -763,10 +788,10 @@ func setSwarmBootstrapNodes(ctx *cli.Context, cfg *node.Config) {
return return
} }
cfg.P2P.BootstrapNodes = []*discover.Node{} cfg.P2P.BootstrapNodes = []*enode.Node{}
for _, url := range SwarmBootnodes { for _, url := range SwarmBootnodes {
node, err := discover.ParseNode(url) node, err := enode.ParseV4(url)
if err != nil { if err != nil {
log.Error("Bootstrap URL invalid", "enode", url, "err", err) log.Error("Bootstrap URL invalid", "enode", url, "err", err)
} }

View file

@ -234,6 +234,7 @@ func existingTestNode(t *testing.T, dir string, bzzaccount string) *testNode {
// start the node // start the node
node.Cmd = runSwarm(t, node.Cmd = runSwarm(t,
"--port", p2pPort, "--port", p2pPort,
"--nat", "extip:127.0.0.1",
"--nodiscover", "--nodiscover",
"--datadir", dir, "--datadir", dir,
"--ipcpath", conf.IPCPath, "--ipcpath", conf.IPCPath,
@ -241,7 +242,7 @@ func existingTestNode(t *testing.T, dir string, bzzaccount string) *testNode {
"--bzzaccount", bzzaccount, "--bzzaccount", bzzaccount,
"--bzznetworkid", "321", "--bzznetworkid", "321",
"--bzzport", httpPort, "--bzzport", httpPort,
"--verbosity", "6", "--verbosity", "3",
) )
node.Cmd.InputLine(testPassphrase) node.Cmd.InputLine(testPassphrase)
defer func() { defer func() {
@ -284,8 +285,8 @@ func existingTestNode(t *testing.T, dir string, bzzaccount string) *testNode {
if err := node.Client.Call(&nodeInfo, "admin_nodeInfo"); err != nil { if err := node.Client.Call(&nodeInfo, "admin_nodeInfo"); err != nil {
t.Fatal(err) t.Fatal(err)
} }
node.Enode = fmt.Sprintf("enode://%s@127.0.0.1:%s", nodeInfo.ID, p2pPort) node.Enode = nodeInfo.Enode
node.IpcPath = conf.IPCPath
return node return node
} }
@ -309,6 +310,7 @@ func newTestNode(t *testing.T, dir string) *testNode {
// start the node // start the node
node.Cmd = runSwarm(t, node.Cmd = runSwarm(t,
"--port", p2pPort, "--port", p2pPort,
"--nat", "extip:127.0.0.1",
"--nodiscover", "--nodiscover",
"--datadir", dir, "--datadir", dir,
"--ipcpath", conf.IPCPath, "--ipcpath", conf.IPCPath,
@ -316,7 +318,7 @@ func newTestNode(t *testing.T, dir string) *testNode {
"--bzzaccount", account.Address.String(), "--bzzaccount", account.Address.String(),
"--bzznetworkid", "321", "--bzznetworkid", "321",
"--bzzport", httpPort, "--bzzport", httpPort,
"--verbosity", "6", "--verbosity", "3",
) )
node.Cmd.InputLine(testPassphrase) node.Cmd.InputLine(testPassphrase)
defer func() { defer func() {
@ -359,9 +361,8 @@ func newTestNode(t *testing.T, dir string) *testNode {
if err := node.Client.Call(&nodeInfo, "admin_nodeInfo"); err != nil { if err := node.Client.Call(&nodeInfo, "admin_nodeInfo"); err != nil {
t.Fatal(err) t.Fatal(err)
} }
node.Enode = fmt.Sprintf("enode://%s@127.0.0.1:%s", nodeInfo.ID, p2pPort) node.Enode = nodeInfo.Enode
node.IpcPath = conf.IPCPath node.IpcPath = conf.IPCPath
return node return node
} }

View file

@ -48,7 +48,7 @@ func main() {
cli.StringFlag{ cli.StringFlag{
Name: "cluster-endpoint", Name: "cluster-endpoint",
Value: "testing", Value: "testing",
Usage: "cluster to point to (open, or testing)", Usage: "cluster to point to (local, open or testing)",
Destination: &cluster, Destination: &cluster,
}, },
cli.IntFlag{ cli.IntFlag{
@ -76,8 +76,8 @@ func main() {
}, },
cli.IntFlag{ cli.IntFlag{
Name: "filesize", Name: "filesize",
Value: 1, Value: 1024,
Usage: "file size for generated random file in MB", Usage: "file size for generated random file in KB",
Destination: &filesize, Destination: &filesize,
}, },
} }

View file

@ -39,6 +39,11 @@ import (
func generateEndpoints(scheme string, cluster string, from int, to int) { func generateEndpoints(scheme string, cluster string, from int, to int) {
if cluster == "prod" { if cluster == "prod" {
cluster = "" cluster = ""
} else if cluster == "local" {
for port := from; port <= to; port++ {
endpoints = append(endpoints, fmt.Sprintf("%s://localhost:%v", scheme, port))
}
return
} else { } else {
cluster = cluster + "." cluster = cluster + "."
} }
@ -53,13 +58,13 @@ func generateEndpoints(scheme string, cluster string, from int, to int) {
} }
func cliUploadAndSync(c *cli.Context) error { func cliUploadAndSync(c *cli.Context) error {
defer func(now time.Time) { log.Info("total time", "time", time.Since(now), "size", filesize) }(time.Now()) defer func(now time.Time) { log.Info("total time", "time", time.Since(now), "size (kb)", filesize) }(time.Now())
generateEndpoints(scheme, cluster, from, to) generateEndpoints(scheme, cluster, from, to)
log.Info("uploading to " + endpoints[0] + " and syncing") log.Info("uploading to " + endpoints[0] + " and syncing")
f, cleanup := generateRandomFile(filesize * 1000000) f, cleanup := generateRandomFile(filesize * 1000)
defer cleanup() defer cleanup()
hash, err := upload(f, endpoints[0]) hash, err := upload(f, endpoints[0])
@ -76,12 +81,7 @@ func cliUploadAndSync(c *cli.Context) error {
log.Info("uploaded successfully", "hash", hash, "digest", fmt.Sprintf("%x", fhash)) log.Info("uploaded successfully", "hash", hash, "digest", fmt.Sprintf("%x", fhash))
if filesize < 10 { time.Sleep(3 * time.Second)
time.Sleep(35 * time.Second)
} else {
time.Sleep(15 * time.Second)
time.Sleep(2 * time.Duration(filesize) * time.Second)
}
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
for _, endpoint := range endpoints { for _, endpoint := range endpoints {
@ -109,7 +109,7 @@ func cliUploadAndSync(c *cli.Context) error {
// fetch is getting the requested `hash` from the `endpoint` and compares it with the `original` file // fetch is getting the requested `hash` from the `endpoint` and compares it with the `original` file
func fetch(hash string, endpoint string, original []byte, ruid string) error { func fetch(hash string, endpoint string, original []byte, ruid string) error {
log.Trace("sleeping", "ruid", ruid) log.Trace("sleeping", "ruid", ruid)
time.Sleep(5 * time.Second) time.Sleep(3 * time.Second)
log.Trace("http get request", "ruid", ruid, "api", endpoint, "hash", hash) log.Trace("http get request", "ruid", ruid, "api", endpoint, "hash", hash)
res, err := http.Get(endpoint + "/bzz:/" + hash + "/") res, err := http.Get(endpoint + "/bzz:/" + hash + "/")

View file

@ -51,8 +51,8 @@ import (
"github.com/ethereum/go-ethereum/metrics/influxdb" "github.com/ethereum/go-ethereum/metrics/influxdb"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/discv5"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/p2p/netutil"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
@ -348,12 +348,12 @@ var (
} }
MinerGasPriceFlag = BigFlag{ MinerGasPriceFlag = BigFlag{
Name: "miner.gasprice", Name: "miner.gasprice",
Usage: "Minimal gas price for mining a transactions", Usage: "Minimum gas price for mining a transaction",
Value: eth.DefaultConfig.MinerGasPrice, Value: eth.DefaultConfig.MinerGasPrice,
} }
MinerLegacyGasPriceFlag = BigFlag{ MinerLegacyGasPriceFlag = BigFlag{
Name: "gasprice", Name: "gasprice",
Usage: "Minimal gas price for mining a transactions (deprecated, use --miner.gasprice)", Usage: "Minimum gas price for mining a transaction (deprecated, use --miner.gasprice)",
Value: eth.DefaultConfig.MinerGasPrice, Value: eth.DefaultConfig.MinerGasPrice,
} }
MinerEtherbaseFlag = cli.StringFlag{ MinerEtherbaseFlag = cli.StringFlag{
@ -572,6 +572,10 @@ var (
Usage: "Minimum POW accepted", Usage: "Minimum POW accepted",
Value: whisper.DefaultMinimumPoW, Value: whisper.DefaultMinimumPoW,
} }
WhisperRestrictConnectionBetweenLightClientsFlag = cli.BoolFlag{
Name: "shh.restrict-light",
Usage: "Restrict connection between two whisper light clients",
}
// Metrics flags // Metrics flags
MetricsEnabledFlag = cli.BoolFlag{ MetricsEnabledFlag = cli.BoolFlag{
@ -611,6 +615,17 @@ var (
Usage: "InfluxDB `host` tag attached to all measurements", Usage: "InfluxDB `host` tag attached to all measurements",
Value: "localhost", Value: "localhost",
} }
EWASMInterpreterFlag = cli.StringFlag{
Name: "vm.ewasm",
Usage: "External ewasm configuration (default = built-in interpreter)",
Value: "",
}
EVMInterpreterFlag = cli.StringFlag{
Name: "vm.evm",
Usage: "External EVM configuration (default = built-in interpreter)",
Value: "",
}
) )
// MakeDataDir retrieves the currently requested data directory, terminating // MakeDataDir retrieves the currently requested data directory, terminating
@ -682,9 +697,9 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
return // already set, don't apply defaults. return // already set, don't apply defaults.
} }
cfg.BootstrapNodes = make([]*discover.Node, 0, len(urls)) cfg.BootstrapNodes = make([]*enode.Node, 0, len(urls))
for _, url := range urls { for _, url := range urls {
node, err := discover.ParseNode(url) node, err := enode.ParseV4(url)
if err != nil { if err != nil {
log.Crit("Bootstrap URL invalid", "enode", url, "err", err) log.Crit("Bootstrap URL invalid", "enode", url, "err", err)
} }
@ -1104,6 +1119,9 @@ func SetShhConfig(ctx *cli.Context, stack *node.Node, cfg *whisper.Config) {
if ctx.GlobalIsSet(WhisperMinPOWFlag.Name) { if ctx.GlobalIsSet(WhisperMinPOWFlag.Name) {
cfg.MinimumAcceptedPOW = ctx.GlobalFloat64(WhisperMinPOWFlag.Name) cfg.MinimumAcceptedPOW = ctx.GlobalFloat64(WhisperMinPOWFlag.Name)
} }
if ctx.GlobalIsSet(WhisperRestrictConnectionBetweenLightClientsFlag.Name) {
cfg.RestrictConnectionBetweenLightClients = true
}
} }
// SetEthConfig applies eth-related command line flags to the config. // SetEthConfig applies eth-related command line flags to the config.
@ -1182,6 +1200,14 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
cfg.EnablePreimageRecording = ctx.GlobalBool(VMEnableDebugFlag.Name) cfg.EnablePreimageRecording = ctx.GlobalBool(VMEnableDebugFlag.Name)
} }
if ctx.GlobalIsSet(EWASMInterpreterFlag.Name) {
cfg.EWASMInterpreter = ctx.GlobalString(EWASMInterpreterFlag.Name)
}
if ctx.GlobalIsSet(EVMInterpreterFlag.Name) {
cfg.EVMInterpreter = ctx.GlobalString(EVMInterpreterFlag.Name)
}
// Override any default configs for hard coded networks. // Override any default configs for hard coded networks.
switch { switch {
case ctx.GlobalBool(TestnetFlag.Name): case ctx.GlobalBool(TestnetFlag.Name):
@ -1377,7 +1403,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai
cache.TrieNodeLimit = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100 cache.TrieNodeLimit = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100
} }
vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)} vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)}
chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg) chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg, nil)
if err != nil { if err != nil {
Fatalf("Can't create BlockChain: %v", err) Fatalf("Can't create BlockChain: %v", err)
} }

View file

@ -41,7 +41,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/whisper/mailserver" "github.com/ethereum/go-ethereum/whisper/mailserver"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6" whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
@ -175,7 +175,7 @@ func initialize() {
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*argVerbosity), log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*argVerbosity), log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
done = make(chan struct{}) done = make(chan struct{})
var peers []*discover.Node var peers []*enode.Node
var err error var err error
if *generateKey { if *generateKey {
@ -203,7 +203,7 @@ func initialize() {
if len(*argEnode) == 0 { if len(*argEnode) == 0 {
argEnode = scanLineA("Please enter the peer's enode: ") argEnode = scanLineA("Please enter the peer's enode: ")
} }
peer := discover.MustParseNode(*argEnode) peer := enode.MustParseV4(*argEnode)
peers = append(peers, peer) peers = append(peers, peer)
} }
@ -747,11 +747,11 @@ func requestExpiredMessagesLoop() {
} }
func extractIDFromEnode(s string) []byte { func extractIDFromEnode(s string) []byte {
n, err := discover.ParseNode(s) n, err := enode.ParseV4(s)
if err != nil { if err != nil {
utils.Fatalf("Failed to parse enode: %s", err) utils.Fatalf("Failed to parse enode: %s", err)
} }
return n.ID[:] return n.ID().Bytes()
} }
// obfuscateBloom adds 16 random bits to the bloom // obfuscateBloom adds 16 random bits to the bloom

View file

@ -100,7 +100,7 @@ func Hex2BytesFixed(str string, flen int) []byte {
return h[len(h)-flen:] return h[len(h)-flen:]
} }
hh := make([]byte, flen) hh := make([]byte, flen)
copy(hh[flen-len(h):flen], h[:]) copy(hh[flen-len(h):flen], h)
return hh return hh
} }

View file

@ -38,3 +38,45 @@ func (d PrettyDuration) String() string {
} }
return label return label
} }
// PrettyAge is a pretty printed version of a time.Duration value that rounds
// the values up to a single most significant unit, days/weeks/years included.
type PrettyAge time.Time
// ageUnits is a list of units the age pretty printing uses.
var ageUnits = []struct {
Size time.Duration
Symbol string
}{
{12 * 30 * 24 * time.Hour, "y"},
{30 * 24 * time.Hour, "mo"},
{7 * 24 * time.Hour, "w"},
{24 * time.Hour, "d"},
{time.Hour, "h"},
{time.Minute, "m"},
{time.Second, "s"},
}
// String implements the Stringer interface, allowing pretty printing of duration
// values rounded to the most significant time unit.
func (t PrettyAge) String() string {
// Calculate the time difference and handle the 0 cornercase
diff := time.Since(time.Time(t))
if diff < time.Second {
return "0"
}
// Accumulate a precision of 3 components before returning
result, prec := "", 0
for _, unit := range ageUnits {
if diff > unit.Size {
result = fmt.Sprintf("%s%d%s", result, diff/unit.Size, unit.Symbol)
diff %= unit.Size
if prec += 1; prec >= 3 {
break
}
}
}
return result
}

View file

@ -34,7 +34,7 @@ import (
const ( const (
// HashLength is the expected length of the hash // HashLength is the expected length of the hash
HashLength = 32 HashLength = 32
// AddressLength is the expected length of the adddress // AddressLength is the expected length of the address
AddressLength = 20 AddressLength = 20
) )

View file

@ -93,27 +93,33 @@ var (
// errMissingSignature is returned if a block's extra-data section doesn't seem // errMissingSignature is returned if a block's extra-data section doesn't seem
// to contain a 65 byte secp256k1 signature. // to contain a 65 byte secp256k1 signature.
errMissingSignature = errors.New("extra-data 65 byte suffix signature missing") errMissingSignature = errors.New("extra-data 65 byte signature suffix missing")
// errExtraSigners is returned if non-checkpoint block contain signer data in // errExtraSigners is returned if non-checkpoint block contain signer data in
// their extra-data fields. // their extra-data fields.
errExtraSigners = errors.New("non-checkpoint block contains extra signer list") errExtraSigners = errors.New("non-checkpoint block contains extra signer list")
// errInvalidCheckpointSigners is returned if a checkpoint block contains an // errInvalidCheckpointSigners is returned if a checkpoint block contains an
// invalid list of signers (i.e. non divisible by 20 bytes, or not the correct // invalid list of signers (i.e. non divisible by 20 bytes).
// ones).
errInvalidCheckpointSigners = errors.New("invalid signer list on checkpoint block") errInvalidCheckpointSigners = errors.New("invalid signer list on checkpoint block")
// errMismatchingCheckpointSigners is returned if a checkpoint block contains a
// list of signers different than the one the local node calculated.
errMismatchingCheckpointSigners = errors.New("mismatching signer list on checkpoint block")
// errInvalidMixDigest is returned if a block's mix digest is non-zero. // errInvalidMixDigest is returned if a block's mix digest is non-zero.
errInvalidMixDigest = errors.New("non-zero mix digest") errInvalidMixDigest = errors.New("non-zero mix digest")
// errInvalidUncleHash is returned if a block contains an non-empty uncle list. // errInvalidUncleHash is returned if a block contains an non-empty uncle list.
errInvalidUncleHash = errors.New("non empty uncle hash") errInvalidUncleHash = errors.New("non empty uncle hash")
// errInvalidDifficulty is returned if the difficulty of a block is not either // errInvalidDifficulty is returned if the difficulty of a block neither 1 or 2.
// of 1 or 2, or if the value does not match the turn of the signer.
errInvalidDifficulty = errors.New("invalid difficulty") errInvalidDifficulty = errors.New("invalid difficulty")
// errWrongDifficulty is returned if the difficulty of a block doesn't match the
// turn of the signer.
errWrongDifficulty = errors.New("wrong difficulty")
// ErrInvalidTimestamp is returned if the timestamp of a block is lower than // ErrInvalidTimestamp is returned if the timestamp of a block is lower than
// the previous block's timestamp + the minimum block period. // the previous block's timestamp + the minimum block period.
ErrInvalidTimestamp = errors.New("invalid timestamp") ErrInvalidTimestamp = errors.New("invalid timestamp")
@ -122,13 +128,12 @@ var (
// be modified via out-of-range or non-contiguous headers. // be modified via out-of-range or non-contiguous headers.
errInvalidVotingChain = errors.New("invalid voting chain") errInvalidVotingChain = errors.New("invalid voting chain")
// errUnauthorized is returned if a header is signed by a non-authorized entity. // errUnauthorizedSigner is returned if a header is signed by a non-authorized entity.
errUnauthorized = errors.New("unauthorized") errUnauthorizedSigner = errors.New("unauthorized signer")
// errWaitTransactions is returned if an empty block is attempted to be sealed // errRecentlySigned is returned if a header is signed by an authorized entity
// on an instant chain (0 second period). It's important to refuse these as the // that already signed a header recently, thus is temporarily not allowed to.
// block reward is zero, so an empty block just bloats the chain... fast. errRecentlySigned = errors.New("recently signed")
errWaitTransactions = errors.New("waiting for transactions")
) )
// SignerFn is a signer callback function to request a hash to be signed by a // SignerFn is a signer callback function to request a hash to be signed by a
@ -205,6 +210,9 @@ type Clique struct {
signer common.Address // Ethereum address of the signing key signer common.Address // Ethereum address of the signing key
signFn SignerFn // Signer function to authorize hashes with signFn SignerFn // Signer function to authorize hashes with
lock sync.RWMutex // Protects the signer fields lock sync.RWMutex // Protects the signer fields
// The fields below are for testing only
fakeDiff bool // Skip difficulty verifications
} }
// New creates a Clique proof-of-authority consensus engine with the initial // New creates a Clique proof-of-authority consensus engine with the initial
@ -359,7 +367,7 @@ func (c *Clique) verifyCascadingFields(chain consensus.ChainReader, header *type
} }
extraSuffix := len(header.Extra) - extraSeal extraSuffix := len(header.Extra) - extraSeal
if !bytes.Equal(header.Extra[extraVanity:extraSuffix], signers) { if !bytes.Equal(header.Extra[extraVanity:extraSuffix], signers) {
return errInvalidCheckpointSigners return errMismatchingCheckpointSigners
} }
} }
// All basic checks passed, verify the seal and return // All basic checks passed, verify the seal and return
@ -388,7 +396,7 @@ func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash commo
} }
} }
// If we're at an checkpoint block, make a snapshot if it's known // If we're at an checkpoint block, make a snapshot if it's known
if number%c.config.Epoch == 0 { if number == 0 || (number%c.config.Epoch == 0 && chain.GetHeaderByNumber(number-1) == nil) {
checkpoint := chain.GetHeaderByNumber(number) checkpoint := chain.GetHeaderByNumber(number)
if checkpoint != nil { if checkpoint != nil {
hash := checkpoint.Hash() hash := checkpoint.Hash()
@ -481,23 +489,25 @@ func (c *Clique) verifySeal(chain consensus.ChainReader, header *types.Header, p
return err return err
} }
if _, ok := snap.Signers[signer]; !ok { if _, ok := snap.Signers[signer]; !ok {
return errUnauthorized return errUnauthorizedSigner
} }
for seen, recent := range snap.Recents { for seen, recent := range snap.Recents {
if recent == signer { if recent == signer {
// Signer is among recents, only fail if the current block doesn't shift it out // Signer is among recents, only fail if the current block doesn't shift it out
if limit := uint64(len(snap.Signers)/2 + 1); seen > number-limit { if limit := uint64(len(snap.Signers)/2 + 1); seen > number-limit {
return errUnauthorized return errRecentlySigned
} }
} }
} }
// Ensure that the difficulty corresponds to the turn-ness of the signer // Ensure that the difficulty corresponds to the turn-ness of the signer
if !c.fakeDiff {
inturn := snap.inturn(header.Number.Uint64(), signer) inturn := snap.inturn(header.Number.Uint64(), signer)
if inturn && header.Difficulty.Cmp(diffInTurn) != 0 { if inturn && header.Difficulty.Cmp(diffInTurn) != 0 {
return errInvalidDifficulty return errWrongDifficulty
} }
if !inturn && header.Difficulty.Cmp(diffNoTurn) != 0 { if !inturn && header.Difficulty.Cmp(diffNoTurn) != 0 {
return errInvalidDifficulty return errWrongDifficulty
}
} }
return nil return nil
} }
@ -600,7 +610,8 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, results c
} }
// For 0-period chains, refuse to seal empty blocks (no reward but would spin sealing) // For 0-period chains, refuse to seal empty blocks (no reward but would spin sealing)
if c.config.Period == 0 && len(block.Transactions()) == 0 { if c.config.Period == 0 && len(block.Transactions()) == 0 {
return errWaitTransactions log.Info("Sealing paused, waiting for transactions")
return nil
} }
// Don't hold the signer fields for the entire sealing procedure // Don't hold the signer fields for the entire sealing procedure
c.lock.RLock() c.lock.RLock()
@ -613,7 +624,7 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, results c
return err return err
} }
if _, authorized := snap.Signers[signer]; !authorized { if _, authorized := snap.Signers[signer]; !authorized {
return errUnauthorized return errUnauthorizedSigner
} }
// If we're amongst the recent signers, wait for the next block // If we're amongst the recent signers, wait for the next block
for seen, recent := range snap.Recents { for seen, recent := range snap.Recents {

View file

@ -57,12 +57,12 @@ type Snapshot struct {
Tally map[common.Address]Tally `json:"tally"` // Current vote tally to avoid recalculating Tally map[common.Address]Tally `json:"tally"` // Current vote tally to avoid recalculating
} }
// signers implements the sort interface to allow sorting a list of addresses // signersAscending implements the sort interface to allow sorting a list of addresses
type signers []common.Address type signersAscending []common.Address
func (s signers) Len() int { return len(s) } func (s signersAscending) Len() int { return len(s) }
func (s signers) Less(i, j int) bool { return bytes.Compare(s[i][:], s[j][:]) < 0 } func (s signersAscending) Less(i, j int) bool { return bytes.Compare(s[i][:], s[j][:]) < 0 }
func (s signers) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s signersAscending) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// newSnapshot creates a new snapshot with the specified startup parameters. This // newSnapshot creates a new snapshot with the specified startup parameters. This
// method does not initialize the set of recent signers, so only ever use if for // method does not initialize the set of recent signers, so only ever use if for
@ -214,11 +214,11 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
return nil, err return nil, err
} }
if _, ok := snap.Signers[signer]; !ok { if _, ok := snap.Signers[signer]; !ok {
return nil, errUnauthorized return nil, errUnauthorizedSigner
} }
for _, recent := range snap.Recents { for _, recent := range snap.Recents {
if recent == signer { if recent == signer {
return nil, errUnauthorized return nil, errRecentlySigned
} }
} }
snap.Recents[number] = signer snap.Recents[number] = signer
@ -298,7 +298,7 @@ func (s *Snapshot) signers() []common.Address {
for sig := range s.Signers { for sig := range s.Signers {
sigs = append(sigs, sig) sigs = append(sigs, sig)
} }
sort.Sort(signers(sigs)) sort.Sort(signersAscending(sigs))
return sigs return sigs
} }

View file

@ -19,24 +19,18 @@ package clique
import ( import (
"bytes" "bytes"
"crypto/ecdsa" "crypto/ecdsa"
"math/big" "sort"
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
type testerVote struct {
signer string
voted string
auth bool
}
// testerAccountPool is a pool to maintain currently active tester accounts, // testerAccountPool is a pool to maintain currently active tester accounts,
// mapped from textual names used in the tests below to actual Ethereum private // mapped from textual names used in the tests below to actual Ethereum private
// keys capable of signing transactions. // keys capable of signing transactions.
@ -50,17 +44,26 @@ func newTesterAccountPool() *testerAccountPool {
} }
} }
func (ap *testerAccountPool) sign(header *types.Header, signer string) { // checkpoint creates a Clique checkpoint signer section from the provided list
// Ensure we have a persistent key for the signer // of authorized signers and embeds it into the provided header.
if ap.accounts[signer] == nil { func (ap *testerAccountPool) checkpoint(header *types.Header, signers []string) {
ap.accounts[signer], _ = crypto.GenerateKey() auths := make([]common.Address, len(signers))
for i, signer := range signers {
auths[i] = ap.address(signer)
}
sort.Sort(signersAscending(auths))
for i, auth := range auths {
copy(header.Extra[extraVanity+i*common.AddressLength:], auth.Bytes())
} }
// Sign the header and embed the signature in extra data
sig, _ := crypto.Sign(sigHash(header).Bytes(), ap.accounts[signer])
copy(header.Extra[len(header.Extra)-65:], sig)
} }
// address retrieves the Ethereum address of a tester account by label, creating
// a new account if no previous one exists yet.
func (ap *testerAccountPool) address(account string) common.Address { func (ap *testerAccountPool) address(account string) common.Address {
// Return the zero account for non-addresses
if account == "" {
return common.Address{}
}
// Ensure we have a persistent key for the account // Ensure we have a persistent key for the account
if ap.accounts[account] == nil { if ap.accounts[account] == nil {
ap.accounts[account], _ = crypto.GenerateKey() ap.accounts[account], _ = crypto.GenerateKey()
@ -69,32 +72,38 @@ func (ap *testerAccountPool) address(account string) common.Address {
return crypto.PubkeyToAddress(ap.accounts[account].PublicKey) return crypto.PubkeyToAddress(ap.accounts[account].PublicKey)
} }
// testerChainReader implements consensus.ChainReader to access the genesis // sign calculates a Clique digital signature for the given block and embeds it
// block. All other methods and requests will panic. // back into the header.
type testerChainReader struct { func (ap *testerAccountPool) sign(header *types.Header, signer string) {
db ethdb.Database // Ensure we have a persistent key for the signer
if ap.accounts[signer] == nil {
ap.accounts[signer], _ = crypto.GenerateKey()
}
// Sign the header and embed the signature in extra data
sig, _ := crypto.Sign(sigHash(header).Bytes(), ap.accounts[signer])
copy(header.Extra[len(header.Extra)-extraSeal:], sig)
} }
func (r *testerChainReader) Config() *params.ChainConfig { return params.AllCliqueProtocolChanges } // testerVote represents a single block signed by a parcitular account, where
func (r *testerChainReader) CurrentHeader() *types.Header { panic("not supported") } // the account may or may not have cast a Clique vote.
func (r *testerChainReader) GetHeader(common.Hash, uint64) *types.Header { panic("not supported") } type testerVote struct {
func (r *testerChainReader) GetBlock(common.Hash, uint64) *types.Block { panic("not supported") } signer string
func (r *testerChainReader) GetHeaderByHash(common.Hash) *types.Header { panic("not supported") } voted string
func (r *testerChainReader) GetHeaderByNumber(number uint64) *types.Header { auth bool
if number == 0 { checkpoint []string
return rawdb.ReadHeader(r.db, rawdb.ReadCanonicalHash(r.db, 0), 0) newbatch bool
}
return nil
} }
// Tests that voting is evaluated correctly for various simple and complex scenarios. // Tests that Clique signer voting is evaluated correctly for various simple and
func TestVoting(t *testing.T) { // complex scenarios, as well as that a few special corner cases fail correctly.
func TestClique(t *testing.T) {
// Define the various voting scenarios to test // Define the various voting scenarios to test
tests := []struct { tests := []struct {
epoch uint64 epoch uint64
signers []string signers []string
votes []testerVote votes []testerVote
results []string results []string
failure error
}{ }{
{ {
// Single signer, no votes cast // Single signer, no votes cast
@ -322,10 +331,49 @@ func TestVoting(t *testing.T) {
votes: []testerVote{ votes: []testerVote{
{signer: "A", voted: "C", auth: true}, {signer: "A", voted: "C", auth: true},
{signer: "B"}, {signer: "B"},
{signer: "A"}, // Checkpoint block, (don't vote here, it's validated outside of snapshots) {signer: "A", checkpoint: []string{"A", "B"}},
{signer: "B", voted: "C", auth: true}, {signer: "B", voted: "C", auth: true},
}, },
results: []string{"A", "B"}, results: []string{"A", "B"},
}, {
// An unauthorized signer should not be able to sign blocks
signers: []string{"A"},
votes: []testerVote{
{signer: "B"},
},
failure: errUnauthorizedSigner,
}, {
// An authorized signer that signed recenty should not be able to sign again
signers: []string{"A", "B"},
votes: []testerVote{
{signer: "A"},
{signer: "A"},
},
failure: errRecentlySigned,
}, {
// Recent signatures should not reset on checkpoint blocks imported in a batch
epoch: 3,
signers: []string{"A", "B", "C"},
votes: []testerVote{
{signer: "A"},
{signer: "B"},
{signer: "A", checkpoint: []string{"A", "B", "C"}},
{signer: "A"},
},
failure: errRecentlySigned,
}, {
// Recent signatures should not reset on checkpoint blocks imported in a new
// batch (https://github.com/ethereum/go-ethereum/issues/17593). Whilst this
// seems overly specific and weird, it was a Rinkeby consensus split.
epoch: 3,
signers: []string{"A", "B", "C"},
votes: []testerVote{
{signer: "A"},
{signer: "B"},
{signer: "A", checkpoint: []string{"A", "B", "C"}},
{signer: "A", newbatch: true},
},
failure: errRecentlySigned,
}, },
} }
// Run through the scenarios and test them // Run through the scenarios and test them
@ -356,28 +404,78 @@ func TestVoting(t *testing.T) {
genesis.Commit(db) genesis.Commit(db)
// Assemble a chain of headers from the cast votes // Assemble a chain of headers from the cast votes
headers := make([]*types.Header, len(tt.votes)) config := *params.TestChainConfig
for j, vote := range tt.votes { config.Clique = &params.CliqueConfig{
headers[j] = &types.Header{ Period: 1,
Number: big.NewInt(int64(j) + 1), Epoch: tt.epoch,
Time: big.NewInt(int64(j) * 15),
Coinbase: accounts.address(vote.voted),
Extra: make([]byte, extraVanity+extraSeal),
} }
engine := New(config.Clique, db)
engine.fakeDiff = true
blocks, _ := core.GenerateChain(&config, genesis.ToBlock(db), engine, db, len(tt.votes), func(j int, gen *core.BlockGen) {
// Cast the vote contained in this block
gen.SetCoinbase(accounts.address(tt.votes[j].voted))
if tt.votes[j].auth {
var nonce types.BlockNonce
copy(nonce[:], nonceAuthVote)
gen.SetNonce(nonce)
}
})
// Iterate through the blocks and seal them individually
for j, block := range blocks {
// Geth the header and prepare it for signing
header := block.Header()
if j > 0 { if j > 0 {
headers[j].ParentHash = headers[j-1].Hash() header.ParentHash = blocks[j-1].Hash()
} }
if vote.auth { header.Extra = make([]byte, extraVanity+extraSeal)
copy(headers[j].Nonce[:], nonceAuthVote) if auths := tt.votes[j].checkpoint; auths != nil {
header.Extra = make([]byte, extraVanity+len(auths)*common.AddressLength+extraSeal)
accounts.checkpoint(header, auths)
} }
accounts.sign(headers[j], vote.signer) header.Difficulty = diffInTurn // Ignored, we just need a valid number
// Generate the signature, embed it into the header and the block
accounts.sign(header, tt.votes[j].signer)
blocks[j] = block.WithSeal(header)
}
// Split the blocks up into individual import batches (cornercase testing)
batches := [][]*types.Block{nil}
for j, block := range blocks {
if tt.votes[j].newbatch {
batches = append(batches, nil)
}
batches[len(batches)-1] = append(batches[len(batches)-1], block)
} }
// Pass all the headers through clique and ensure tallying succeeds // Pass all the headers through clique and ensure tallying succeeds
head := headers[len(headers)-1] chain, err := core.NewBlockChain(db, nil, &config, engine, vm.Config{}, nil)
snap, err := New(&params.CliqueConfig{Epoch: tt.epoch}, db).snapshot(&testerChainReader{db: db}, head.Number.Uint64(), head.Hash(), headers)
if err != nil { if err != nil {
t.Errorf("test %d: failed to create voting snapshot: %v", i, err) t.Errorf("test %d: failed to create test chain: %v", i, err)
continue
}
failed := false
for j := 0; j < len(batches)-1; j++ {
if k, err := chain.InsertChain(batches[j]); err != nil {
t.Errorf("test %d: failed to import batch %d, block %d: %v", i, j, k, err)
failed = true
break
}
}
if failed {
continue
}
if _, err = chain.InsertChain(batches[len(batches)-1]); err != tt.failure {
t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure)
}
if tt.failure != nil {
continue
}
// No failure was produced or requested, generate the final voting snapshot
head := blocks[len(blocks)-1]
snap, err := engine.snapshot(chain, head.NumberU64(), head.Hash(), nil)
if err != nil {
t.Errorf("test %d: failed to retrieve voting snapshot: %v", i, err)
continue continue
} }
// Verify the final list of signers against the expected ones // Verify the final list of signers against the expected ones

View file

@ -90,7 +90,7 @@ type Engine interface {
// the result into the given channel. // the result into the given channel.
// //
// Note, the method returns immediately and will send the result async. More // Note, the method returns immediately and will send the result async. More
// than one result may also be returned depending on the consensus algorothm. // than one result may also be returned depending on the consensus algorithm.
Seal(chain ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error Seal(chain ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error
// SealHash returns the hash of a block prior to it being sealed. // SealHash returns the hash of a block prior to it being sealed.

View file

@ -40,8 +40,22 @@ import (
var ( var (
FrontierBlockReward = big.NewInt(5e+18) // Block reward in wei for successfully mining a block FrontierBlockReward = big.NewInt(5e+18) // Block reward in wei for successfully mining a block
ByzantiumBlockReward = big.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Byzantium ByzantiumBlockReward = big.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Byzantium
ConstantinopleBlockReward = big.NewInt(2e+18) // Block reward in wei for successfully mining a block upward from Constantinople
maxUncles = 2 // Maximum number of uncles allowed in a single block maxUncles = 2 // Maximum number of uncles allowed in a single block
allowedFutureBlockTime = 15 * time.Second // Max time from current time allowed for blocks, before they're considered future blocks allowedFutureBlockTime = 15 * time.Second // Max time from current time allowed for blocks, before they're considered future blocks
// calcDifficultyConstantinople is the difficulty adjustment algorithm for Constantinople.
// It returns the difficulty that a new block should have when created at time given the
// parent block's time and difficulty. The calculation uses the Byzantium rules, but with
// bomb offset 5M.
// Specification EIP-1234: https://eips.ethereum.org/EIPS/eip-1234
calcDifficultyConstantinople = makeDifficultyCalculator(big.NewInt(5000000))
// calcDifficultyByzantium is the difficulty adjustment algorithm. It returns
// the difficulty that a new block should have when created at time given the
// parent block's time and difficulty. The calculation uses the Byzantium rules.
// Specification EIP-649: https://eips.ethereum.org/EIPS/eip-649
calcDifficultyByzantium = makeDifficultyCalculator(big.NewInt(3000000))
) )
// Various error messages to mark blocks invalid. These should be private to // Various error messages to mark blocks invalid. These should be private to
@ -299,6 +313,8 @@ func (ethash *Ethash) CalcDifficulty(chain consensus.ChainReader, time uint64, p
func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int { func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int {
next := new(big.Int).Add(parent.Number, big1) next := new(big.Int).Add(parent.Number, big1)
switch { switch {
case config.IsConstantinople(next):
return calcDifficultyConstantinople(time, parent)
case config.IsByzantium(next): case config.IsByzantium(next):
return calcDifficultyByzantium(time, parent) return calcDifficultyByzantium(time, parent)
case config.IsHomestead(next): case config.IsHomestead(next):
@ -316,13 +332,16 @@ var (
big9 = big.NewInt(9) big9 = big.NewInt(9)
big10 = big.NewInt(10) big10 = big.NewInt(10)
bigMinus99 = big.NewInt(-99) bigMinus99 = big.NewInt(-99)
big2999999 = big.NewInt(2999999)
) )
// calcDifficultyByzantium is the difficulty adjustment algorithm. It returns // makeDifficultyCalculator creates a difficultyCalculator with the given bomb-delay.
// the difficulty that a new block should have when created at time given the // the difficulty is calculated with Byzantium rules, which differs from Homestead in
// parent block's time and difficulty. The calculation uses the Byzantium rules. // how uncles affect the calculation
func calcDifficultyByzantium(time uint64, parent *types.Header) *big.Int { func makeDifficultyCalculator(bombDelay *big.Int) func(time uint64, parent *types.Header) *big.Int {
// Note, the calculations below looks at the parent number, which is 1 below
// the block number. Thus we remove one from the delay given
bombDelayFromParent := new(big.Int).Sub(bombDelay, big1)
return func(time uint64, parent *types.Header) *big.Int {
// https://github.com/ethereum/EIPs/issues/100. // https://github.com/ethereum/EIPs/issues/100.
// algorithm: // algorithm:
// diff = (parent_diff + // diff = (parent_diff +
@ -357,12 +376,11 @@ func calcDifficultyByzantium(time uint64, parent *types.Header) *big.Int {
if x.Cmp(params.MinimumDifficulty) < 0 { if x.Cmp(params.MinimumDifficulty) < 0 {
x.Set(params.MinimumDifficulty) x.Set(params.MinimumDifficulty)
} }
// calculate a fake block number for the ice-age delay: // calculate a fake block number for the ice-age delay
// https://github.com/ethereum/EIPs/pull/669 // Specification: https://eips.ethereum.org/EIPS/eip-1234
// fake_block_number = max(0, block.number - 3_000_000)
fakeBlockNumber := new(big.Int) fakeBlockNumber := new(big.Int)
if parent.Number.Cmp(big2999999) >= 0 { if parent.Number.Cmp(bombDelayFromParent) >= 0 {
fakeBlockNumber = fakeBlockNumber.Sub(parent.Number, big2999999) // Note, parent is 1 less than the actual block number fakeBlockNumber = fakeBlockNumber.Sub(parent.Number, bombDelayFromParent)
} }
// for the exponential factor // for the exponential factor
periodCount := fakeBlockNumber periodCount := fakeBlockNumber
@ -377,6 +395,7 @@ func calcDifficultyByzantium(time uint64, parent *types.Header) *big.Int {
} }
return x return x
} }
}
// calcDifficultyHomestead is the difficulty adjustment algorithm. It returns // calcDifficultyHomestead is the difficulty adjustment algorithm. It returns
// the difficulty that a new block should have when created at time given the // the difficulty that a new block should have when created at time given the
@ -592,6 +611,9 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header
if config.IsByzantium(header.Number) { if config.IsByzantium(header.Number) {
blockReward = ByzantiumBlockReward blockReward = ByzantiumBlockReward
} }
if config.IsConstantinople(header.Number) {
blockReward = ConstantinopleBlockReward
}
// Accumulate the rewards for the miner and any included uncles // Accumulate the rewards for the miner and any included uncles
reward := new(big.Int).Set(blockReward) reward := new(big.Int).Set(blockReward)
r := new(big.Int) r := new(big.Int)

View file

@ -485,7 +485,7 @@ func New(config Config, notify []string, noverify bool) *Ethash {
caches: newlru("cache", config.CachesInMem, newCache), caches: newlru("cache", config.CachesInMem, newCache),
datasets: newlru("dataset", config.DatasetsInMem, newDataset), datasets: newlru("dataset", config.DatasetsInMem, newDataset),
update: make(chan struct{}), update: make(chan struct{}),
hashrate: metrics.NewMeter(), hashrate: metrics.NewMeterForced(),
workCh: make(chan *sealTask), workCh: make(chan *sealTask),
fetchWorkCh: make(chan *sealWork), fetchWorkCh: make(chan *sealWork),
submitWorkCh: make(chan *mineResult), submitWorkCh: make(chan *mineResult),
@ -505,7 +505,7 @@ func NewTester(notify []string, noverify bool) *Ethash {
caches: newlru("cache", 1, newCache), caches: newlru("cache", 1, newCache),
datasets: newlru("dataset", 1, newDataset), datasets: newlru("dataset", 1, newDataset),
update: make(chan struct{}), update: make(chan struct{}),
hashrate: metrics.NewMeter(), hashrate: metrics.NewMeterForced(),
workCh: make(chan *sealTask), workCh: make(chan *sealTask),
fetchWorkCh: make(chan *sealWork), fetchWorkCh: make(chan *sealWork),
submitWorkCh: make(chan *mineResult), submitWorkCh: make(chan *mineResult),

View file

@ -40,6 +40,18 @@ func TestRemoteNotify(t *testing.T) {
go server.Serve(listener) go server.Serve(listener)
// Wait for server to start listening
var tries int
for tries = 0; tries < 10; tries++ {
conn, _ := net.DialTimeout("tcp", listener.Addr().String(), 1*time.Second)
if conn != nil {
break
}
}
if tries == 10 {
t.Fatal("tcp listener not ready for more than 10 seconds")
}
// Create the custom ethash engine // Create the custom ethash engine
ethash := NewTester([]string{"http://" + listener.Addr().String()}, false) ethash := NewTester([]string{"http://" + listener.Addr().String()}, false)
defer ethash.Close() defer ethash.Close()
@ -61,7 +73,7 @@ func TestRemoteNotify(t *testing.T) {
if want := common.BytesToHash(target.Bytes()).Hex(); work[2] != want { if want := common.BytesToHash(target.Bytes()).Hex(); work[2] != want {
t.Errorf("work packet target mismatch: have %s, want %s", work[2], want) t.Errorf("work packet target mismatch: have %s, want %s", work[2], want)
} }
case <-time.After(time.Second): case <-time.After(3 * time.Second):
t.Fatalf("notification timed out") t.Fatalf("notification timed out")
} }
} }
@ -108,7 +120,7 @@ func TestRemoteMultiNotify(t *testing.T) {
for i := 0; i < cap(sink); i++ { for i := 0; i < cap(sink); i++ {
select { select {
case <-sink: case <-sink:
case <-time.After(250 * time.Millisecond): case <-time.After(3 * time.Second):
t.Fatalf("notification %d timed out", i) t.Fatalf("notification %d timed out", i)
} }
} }

View file

@ -175,7 +175,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
// Time the insertion of the new chain. // Time the insertion of the new chain.
// State and blocks are stored in the same DB. // State and blocks are stored in the same DB.
chainman, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}) chainman, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
defer chainman.Stop() defer chainman.Stop()
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
@ -287,7 +287,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
if err != nil { if err != nil {
b.Fatalf("error opening database at %v: %v", dir, err) b.Fatalf("error opening database at %v: %v", dir, err)
} }
chain, err := NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}) chain, err := NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil)
if err != nil { if err != nil {
b.Fatalf("error creating chain: %v", err) b.Fatalf("error creating chain: %v", err)
} }

View file

@ -42,7 +42,7 @@ func TestHeaderVerification(t *testing.T) {
headers[i] = block.Header() headers[i] = block.Header()
} }
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces // Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}) chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil)
defer chain.Stop() defer chain.Stop()
for i := 0; i < len(blocks); i++ { for i := 0; i < len(blocks); i++ {
@ -106,11 +106,11 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) {
var results <-chan error var results <-chan error
if valid { if valid {
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}) chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil)
_, results = chain.engine.VerifyHeaders(chain, headers, seals) _, results = chain.engine.VerifyHeaders(chain, headers, seals)
chain.Stop() chain.Stop()
} else { } else {
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}) chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}, nil)
_, results = chain.engine.VerifyHeaders(chain, headers, seals) _, results = chain.engine.VerifyHeaders(chain, headers, seals)
chain.Stop() chain.Stop()
} }
@ -173,7 +173,7 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
defer runtime.GOMAXPROCS(old) defer runtime.GOMAXPROCS(old)
// Start the verifications and immediately abort // Start the verifications and immediately abort
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}) chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}, nil)
defer chain.Stop() defer chain.Stop()
abort, results := chain.engine.VerifyHeaders(chain, headers, seals) abort, results := chain.engine.VerifyHeaders(chain, headers, seals)

View file

@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/mclock" "github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/common/prque"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
@ -43,7 +44,6 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/hashicorp/golang-lru" "github.com/hashicorp/golang-lru"
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
) )
var ( var (
@ -129,12 +129,13 @@ type BlockChain struct {
vmConfig vm.Config vmConfig vm.Config
badBlocks *lru.Cache // Bad block cache badBlocks *lru.Cache // Bad block cache
shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block.
} }
// NewBlockChain returns a fully initialised block chain using information // NewBlockChain returns a fully initialised block chain using information
// available in the database. It initialises the default Ethereum Validator and // available in the database. It initialises the default Ethereum Validator and
// Processor. // Processor.
func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config) (*BlockChain, error) { func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(block *types.Block) bool) (*BlockChain, error) {
if cacheConfig == nil { if cacheConfig == nil {
cacheConfig = &CacheConfig{ cacheConfig = &CacheConfig{
TrieNodeLimit: 256 * 1024 * 1024, TrieNodeLimit: 256 * 1024 * 1024,
@ -151,9 +152,10 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
chainConfig: chainConfig, chainConfig: chainConfig,
cacheConfig: cacheConfig, cacheConfig: cacheConfig,
db: db, db: db,
triegc: prque.New(), triegc: prque.New(nil),
stateCache: state.NewDatabase(db), stateCache: state.NewDatabase(db),
quit: make(chan struct{}), quit: make(chan struct{}),
shouldPreserve: shouldPreserve,
bodyCache: bodyCache, bodyCache: bodyCache,
bodyRLPCache: bodyRLPCache, bodyRLPCache: bodyRLPCache,
blockCache: blockCache, blockCache: blockCache,
@ -251,9 +253,9 @@ func (bc *BlockChain) loadLastState() error {
blockTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) blockTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
fastTd := bc.GetTd(currentFastBlock.Hash(), currentFastBlock.NumberU64()) fastTd := bc.GetTd(currentFastBlock.Hash(), currentFastBlock.NumberU64())
log.Info("Loaded most recent local header", "number", currentHeader.Number, "hash", currentHeader.Hash(), "td", headerTd) log.Info("Loaded most recent local header", "number", currentHeader.Number, "hash", currentHeader.Hash(), "td", headerTd, "age", common.PrettyAge(time.Unix(currentHeader.Time.Int64(), 0)))
log.Info("Loaded most recent local full block", "number", currentBlock.Number(), "hash", currentBlock.Hash(), "td", blockTd) log.Info("Loaded most recent local full block", "number", currentBlock.Number(), "hash", currentBlock.Hash(), "td", blockTd, "age", common.PrettyAge(time.Unix(currentBlock.Time().Int64(), 0)))
log.Info("Loaded most recent local fast block", "number", currentFastBlock.Number(), "hash", currentFastBlock.Hash(), "td", fastTd) log.Info("Loaded most recent local fast block", "number", currentFastBlock.Number(), "hash", currentFastBlock.Hash(), "td", fastTd, "age", common.PrettyAge(time.Unix(currentFastBlock.Time().Int64(), 0)))
return nil return nil
} }
@ -850,13 +852,16 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
} }
bc.mu.Unlock() bc.mu.Unlock()
log.Info("Imported new block receipts", context := []interface{}{
"count", stats.processed, "count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)),
"elapsed", common.PrettyDuration(time.Since(start)), "number", head.Number(), "hash", head.Hash(), "age", common.PrettyAge(time.Unix(head.Time().Int64(), 0)),
"number", head.Number(),
"hash", head.Hash(),
"size", common.StorageSize(bytes), "size", common.StorageSize(bytes),
"ignored", stats.ignored) }
if stats.ignored > 0 {
context = append(context, []interface{}{"ignored", stats.ignored}...)
}
log.Info("Imported new block receipts", context...)
return 0, nil return 0, nil
} }
@ -915,7 +920,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
} else { } else {
// Full but not archive node, do proper garbage collection // Full but not archive node, do proper garbage collection
triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
bc.triegc.Push(root, -float32(block.NumberU64())) bc.triegc.Push(root, -int64(block.NumberU64()))
if current := block.NumberU64(); current > triesInMemory { if current := block.NumberU64(); current > triesInMemory {
// If we exceeded our memory allowance, flush matured singleton nodes to disk // If we exceeded our memory allowance, flush matured singleton nodes to disk
@ -964,8 +969,17 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
reorg := externTd.Cmp(localTd) > 0 reorg := externTd.Cmp(localTd) > 0
currentBlock = bc.CurrentBlock() currentBlock = bc.CurrentBlock()
if !reorg && externTd.Cmp(localTd) == 0 { if !reorg && externTd.Cmp(localTd) == 0 {
// Split same-difficulty blocks by number, then at random // Split same-difficulty blocks by number, then preferentially select
reorg = block.NumberU64() < currentBlock.NumberU64() || (block.NumberU64() == currentBlock.NumberU64() && mrand.Float64() < 0.5) // the block generated by the local miner as the canonical block.
if block.NumberU64() < currentBlock.NumberU64() {
reorg = true
} else if block.NumberU64() == currentBlock.NumberU64() {
var currentPreserve, blockPreserve bool
if bc.shouldPreserve != nil {
currentPreserve, blockPreserve = bc.shouldPreserve(currentBlock), bc.shouldPreserve(block)
}
reorg = !currentPreserve && (blockPreserve || mrand.Float64() < 0.5)
}
} }
if reorg { if reorg {
// Reorganise the chain if the parent is not the head block // Reorganise the chain if the parent is not the head block
@ -1229,8 +1243,13 @@ func (st *insertStats) report(chain []*types.Block, index int, cache common.Stor
context := []interface{}{ context := []interface{}{
"blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000, "blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000,
"elapsed", common.PrettyDuration(elapsed), "mgasps", float64(st.usedGas) * 1000 / float64(elapsed), "elapsed", common.PrettyDuration(elapsed), "mgasps", float64(st.usedGas) * 1000 / float64(elapsed),
"number", end.Number(), "hash", end.Hash(), "cache", cache, "number", end.Number(), "hash", end.Hash(),
} }
if timestamp := time.Unix(end.Time().Int64(), 0); time.Since(timestamp) > time.Minute {
context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...)
}
context = append(context, []interface{}{"cache", cache}...)
if st.queued > 0 { if st.queued > 0 {
context = append(context, []interface{}{"queued", st.queued}...) context = append(context, []interface{}{"queued", st.queued}...)
} }

View file

@ -52,7 +52,7 @@ func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *B
) )
// Initialize a fresh chain with only a genesis block // Initialize a fresh chain with only a genesis block
blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}) blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil)
// Create and inject the requested chain // Create and inject the requested chain
if n == 0 { if n == 0 {
return db, blockchain, nil return db, blockchain, nil
@ -523,7 +523,7 @@ func testReorgBadHashes(t *testing.T, full bool) {
blockchain.Stop() blockchain.Stop()
// Create a new BlockChain and check that it rolled back the state. // Create a new BlockChain and check that it rolled back the state.
ncm, err := NewBlockChain(blockchain.db, nil, blockchain.chainConfig, ethash.NewFaker(), vm.Config{}) ncm, err := NewBlockChain(blockchain.db, nil, blockchain.chainConfig, ethash.NewFaker(), vm.Config{}, nil)
if err != nil { if err != nil {
t.Fatalf("failed to create new chain manager: %v", err) t.Fatalf("failed to create new chain manager: %v", err)
} }
@ -635,7 +635,7 @@ func TestFastVsFullChains(t *testing.T) {
// Import the chain as an archive node for the comparison baseline // Import the chain as an archive node for the comparison baseline
archiveDb := ethdb.NewMemDatabase() archiveDb := ethdb.NewMemDatabase()
gspec.MustCommit(archiveDb) gspec.MustCommit(archiveDb)
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}) archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
defer archive.Stop() defer archive.Stop()
if n, err := archive.InsertChain(blocks); err != nil { if n, err := archive.InsertChain(blocks); err != nil {
@ -644,7 +644,7 @@ func TestFastVsFullChains(t *testing.T) {
// Fast import the chain as a non-archive node to test // Fast import the chain as a non-archive node to test
fastDb := ethdb.NewMemDatabase() fastDb := ethdb.NewMemDatabase()
gspec.MustCommit(fastDb) gspec.MustCommit(fastDb)
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}) fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
defer fast.Stop() defer fast.Stop()
headers := make([]*types.Header, len(blocks)) headers := make([]*types.Header, len(blocks))
@ -722,7 +722,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
archiveDb := ethdb.NewMemDatabase() archiveDb := ethdb.NewMemDatabase()
gspec.MustCommit(archiveDb) gspec.MustCommit(archiveDb)
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}) archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
if n, err := archive.InsertChain(blocks); err != nil { if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err) t.Fatalf("failed to process block %d: %v", n, err)
} }
@ -735,7 +735,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
// Import the chain as a non-archive node and ensure all pointers are updated // Import the chain as a non-archive node and ensure all pointers are updated
fastDb := ethdb.NewMemDatabase() fastDb := ethdb.NewMemDatabase()
gspec.MustCommit(fastDb) gspec.MustCommit(fastDb)
fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}) fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
defer fast.Stop() defer fast.Stop()
headers := make([]*types.Header, len(blocks)) headers := make([]*types.Header, len(blocks))
@ -756,7 +756,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
lightDb := ethdb.NewMemDatabase() lightDb := ethdb.NewMemDatabase()
gspec.MustCommit(lightDb) gspec.MustCommit(lightDb)
light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}) light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
if n, err := light.InsertHeaderChain(headers, 1); err != nil { if n, err := light.InsertHeaderChain(headers, 1); err != nil {
t.Fatalf("failed to insert header %d: %v", n, err) t.Fatalf("failed to insert header %d: %v", n, err)
} }
@ -825,7 +825,7 @@ func TestChainTxReorgs(t *testing.T) {
} }
}) })
// Import the chain. This runs all block validation rules. // Import the chain. This runs all block validation rules.
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
if i, err := blockchain.InsertChain(chain); err != nil { if i, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert original chain[%d]: %v", i, err) t.Fatalf("failed to insert original chain[%d]: %v", i, err)
} }
@ -896,7 +896,7 @@ func TestLogReorgs(t *testing.T) {
signer = types.NewEIP155Signer(gspec.Config.ChainID) signer = types.NewEIP155Signer(gspec.Config.ChainID)
) )
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
defer blockchain.Stop() defer blockchain.Stop()
rmLogsCh := make(chan RemovedLogsEvent) rmLogsCh := make(chan RemovedLogsEvent)
@ -943,7 +943,7 @@ func TestReorgSideEvent(t *testing.T) {
signer = types.NewEIP155Signer(gspec.Config.ChainID) signer = types.NewEIP155Signer(gspec.Config.ChainID)
) )
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
defer blockchain.Stop() defer blockchain.Stop()
chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {}) chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {})
@ -1072,7 +1072,7 @@ func TestEIP155Transition(t *testing.T) {
genesis = gspec.MustCommit(db) genesis = gspec.MustCommit(db)
) )
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
defer blockchain.Stop() defer blockchain.Stop()
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, block *BlockGen) { blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, block *BlockGen) {
@ -1179,7 +1179,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
} }
genesis = gspec.MustCommit(db) genesis = gspec.MustCommit(db)
) )
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
defer blockchain.Stop() defer blockchain.Stop()
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, block *BlockGen) { blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, block *BlockGen) {
@ -1254,7 +1254,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
diskdb := ethdb.NewMemDatabase() diskdb := ethdb.NewMemDatabase()
new(Genesis).MustCommit(diskdb) new(Genesis).MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}) chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
if err != nil { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) t.Fatalf("failed to create tester chain: %v", err)
} }
@ -1298,7 +1298,7 @@ func TestTrieForkGC(t *testing.T) {
diskdb := ethdb.NewMemDatabase() diskdb := ethdb.NewMemDatabase()
new(Genesis).MustCommit(diskdb) new(Genesis).MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}) chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
if err != nil { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) t.Fatalf("failed to create tester chain: %v", err)
} }
@ -1337,7 +1337,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
diskdb := ethdb.NewMemDatabase() diskdb := ethdb.NewMemDatabase()
new(Genesis).MustCommit(diskdb) new(Genesis).MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}) chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
if err != nil { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) t.Fatalf("failed to create tester chain: %v", err)
} }
@ -1419,7 +1419,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in
diskdb := ethdb.NewMemDatabase() diskdb := ethdb.NewMemDatabase()
gspec.MustCommit(diskdb) gspec.MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}) chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
if err != nil { if err != nil {
b.Fatalf("failed to create tester chain: %v", err) b.Fatalf("failed to create tester chain: %v", err)
} }

View file

@ -445,7 +445,7 @@ func (c *ChainIndexer) AddChildIndexer(indexer *ChainIndexer) {
func (c *ChainIndexer) loadValidSections() { func (c *ChainIndexer) loadValidSections() {
data, _ := c.indexDb.Get([]byte("count")) data, _ := c.indexDb.Get([]byte("count"))
if len(data) == 8 { if len(data) == 8 {
c.storedSections = binary.BigEndian.Uint64(data[:]) c.storedSections = binary.BigEndian.Uint64(data)
} }
} }

View file

@ -67,6 +67,11 @@ func (b *BlockGen) SetExtra(data []byte) {
b.header.Extra = data b.header.Extra = data
} }
// SetNonce sets the nonce field of the generated block.
func (b *BlockGen) SetNonce(nonce types.BlockNonce) {
b.header.Nonce = nonce
}
// AddTx adds a transaction to the generated block. If no coinbase has // AddTx adds a transaction to the generated block. If no coinbase has
// been set, the block's coinbase is set to the zero address. // been set, the block's coinbase is set to the zero address.
// //
@ -172,7 +177,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, types.Receipts) { genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, types.Receipts) {
// TODO(karalabe): This is needed for clique, which depends on multiple blocks. // TODO(karalabe): This is needed for clique, which depends on multiple blocks.
// It's nonetheless ugly to spin up a blockchain here. Get rid of this somehow. // It's nonetheless ugly to spin up a blockchain here. Get rid of this somehow.
blockchain, _ := NewBlockChain(db, nil, config, engine, vm.Config{}) blockchain, _ := NewBlockChain(db, nil, config, engine, vm.Config{}, nil)
defer blockchain.Stop() defer blockchain.Stop()
b := &BlockGen{i: i, parent: parent, chain: blocks, chainReader: blockchain, statedb: statedb, config: config, engine: engine} b := &BlockGen{i: i, parent: parent, chain: blocks, chainReader: blockchain, statedb: statedb, config: config, engine: engine}
@ -190,13 +195,14 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(b.header.Number) == 0 { if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(b.header.Number) == 0 {
misc.ApplyDAOHardFork(statedb) misc.ApplyDAOHardFork(statedb)
} }
// Execute any user modifications to the block and finalize it // Execute any user modifications to the block
if gen != nil { if gen != nil {
gen(i, b) gen(i, b)
} }
if b.engine != nil { if b.engine != nil {
// Finalize and seal the block
block, _ := b.engine.Finalize(b.chainReader, b.header, statedb, b.txs, b.uncles, b.receipts) block, _ := b.engine.Finalize(b.chainReader, b.header, statedb, b.txs, b.uncles, b.receipts)
// Write state changes to db // Write state changes to db
root, err := statedb.Commit(config.IsEIP158(b.header.Number)) root, err := statedb.Commit(config.IsEIP158(b.header.Number))
if err != nil { if err != nil {

View file

@ -79,7 +79,7 @@ func ExampleGenerateChain() {
}) })
// Import the chain. This runs all block validation rules. // Import the chain. This runs all block validation rules.
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
defer blockchain.Stop() defer blockchain.Stop()
if i, err := blockchain.InsertChain(chain); err != nil { if i, err := blockchain.InsertChain(chain); err != nil {

View file

@ -45,7 +45,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
proConf.DAOForkBlock = forkBlock proConf.DAOForkBlock = forkBlock
proConf.DAOForkSupport = true proConf.DAOForkSupport = true
proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{}) proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil)
defer proBc.Stop() defer proBc.Stop()
conDb := ethdb.NewMemDatabase() conDb := ethdb.NewMemDatabase()
@ -55,7 +55,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
conConf.DAOForkBlock = forkBlock conConf.DAOForkBlock = forkBlock
conConf.DAOForkSupport = false conConf.DAOForkSupport = false
conBc, _ := NewBlockChain(conDb, nil, &conConf, ethash.NewFaker(), vm.Config{}) conBc, _ := NewBlockChain(conDb, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil)
defer conBc.Stop() defer conBc.Stop()
if _, err := proBc.InsertChain(prefix); err != nil { if _, err := proBc.InsertChain(prefix); err != nil {
@ -69,7 +69,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Create a pro-fork block, and try to feed into the no-fork chain // Create a pro-fork block, and try to feed into the no-fork chain
db = ethdb.NewMemDatabase() db = ethdb.NewMemDatabase()
gspec.MustCommit(db) gspec.MustCommit(db)
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}) bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil)
defer bc.Stop() defer bc.Stop()
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64())) blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
@ -94,7 +94,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Create a no-fork block, and try to feed into the pro-fork chain // Create a no-fork block, and try to feed into the pro-fork chain
db = ethdb.NewMemDatabase() db = ethdb.NewMemDatabase()
gspec.MustCommit(db) gspec.MustCommit(db)
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}) bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil)
defer bc.Stop() defer bc.Stop()
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64())) blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
@ -120,7 +120,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Verify that contra-forkers accept pro-fork extra-datas after forking finishes // Verify that contra-forkers accept pro-fork extra-datas after forking finishes
db = ethdb.NewMemDatabase() db = ethdb.NewMemDatabase()
gspec.MustCommit(db) gspec.MustCommit(db)
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}) bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil)
defer bc.Stop() defer bc.Stop()
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64())) blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
@ -140,7 +140,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Verify that pro-forkers accept contra-fork extra-datas after forking finishes // Verify that pro-forkers accept contra-fork extra-datas after forking finishes
db = ethdb.NewMemDatabase() db = ethdb.NewMemDatabase()
gspec.MustCommit(db) gspec.MustCommit(db)
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}) bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil)
defer bc.Stop() defer bc.Stop()
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64())) blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))

View file

@ -120,7 +120,7 @@ func TestSetupGenesis(t *testing.T) {
// Advance to block #4, past the homestead transition block of customg. // Advance to block #4, past the homestead transition block of customg.
genesis := oldcustomg.MustCommit(db) genesis := oldcustomg.MustCommit(db)
bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{}) bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{}, nil)
defer bc.Stop() defer bc.Stop()
blocks, _ := GenerateChain(oldcustomg.Config, genesis, ethash.NewFaker(), db, 4, nil) blocks, _ := GenerateChain(oldcustomg.Config, genesis, ethash.NewFaker(), db, 4, nil)

View file

@ -281,8 +281,18 @@ func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, writeHeader WhCa
} }
// Report some public statistics so the user has a clue what's going on // Report some public statistics so the user has a clue what's going on
last := chain[len(chain)-1] last := chain[len(chain)-1]
log.Info("Imported new block headers", "count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)),
"number", last.Number, "hash", last.Hash(), "ignored", stats.ignored) context := []interface{}{
"count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)),
"number", last.Number, "hash", last.Hash(),
}
if timestamp := time.Unix(last.Time.Int64(), 0); time.Since(timestamp) > time.Minute {
context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...)
}
if stats.ignored > 0 {
context = append(context, []interface{}{"ignored", stats.ignored}...)
}
log.Info("Imported new block headers", context...)
return 0, nil return 0, nil
} }

View file

@ -77,7 +77,7 @@ type stateObject struct {
trie Trie // storage trie, which becomes non-nil on first access trie Trie // storage trie, which becomes non-nil on first access
code Code // contract bytecode, which gets set when code is loaded code Code // contract bytecode, which gets set when code is loaded
cachedStorage Storage // Storage entry cache to avoid duplicate reads originStorage Storage // Storage cache of original entries to dedup rewrites
dirtyStorage Storage // Storage entries that need to be flushed to disk dirtyStorage Storage // Storage entries that need to be flushed to disk
// Cache flags. // Cache flags.
@ -115,7 +115,7 @@ func newObject(db *StateDB, address common.Address, data Account) *stateObject {
address: address, address: address,
addrHash: crypto.Keccak256Hash(address[:]), addrHash: crypto.Keccak256Hash(address[:]),
data: data, data: data,
cachedStorage: make(Storage), originStorage: make(Storage),
dirtyStorage: make(Storage), dirtyStorage: make(Storage),
} }
} }
@ -159,13 +159,25 @@ func (c *stateObject) getTrie(db Database) Trie {
return c.trie return c.trie
} }
// GetState returns a value in account storage. // GetState retrieves a value from the account storage trie.
func (self *stateObject) GetState(db Database, key common.Hash) common.Hash { func (self *stateObject) GetState(db Database, key common.Hash) common.Hash {
value, exists := self.cachedStorage[key] // If we have a dirty value for this state entry, return it
if exists { value, dirty := self.dirtyStorage[key]
if dirty {
return value return value
} }
// Load from DB in case it is missing. // Otherwise return the entry's original value
return self.GetCommittedState(db, key)
}
// GetCommittedState retrieves a value from the committed account storage trie.
func (self *stateObject) GetCommittedState(db Database, key common.Hash) common.Hash {
// If we have the original value cached, return that
value, cached := self.originStorage[key]
if cached {
return value
}
// Otherwise load the value from the database
enc, err := self.getTrie(db).TryGet(key[:]) enc, err := self.getTrie(db).TryGet(key[:])
if err != nil { if err != nil {
self.setError(err) self.setError(err)
@ -178,22 +190,27 @@ func (self *stateObject) GetState(db Database, key common.Hash) common.Hash {
} }
value.SetBytes(content) value.SetBytes(content)
} }
self.cachedStorage[key] = value self.originStorage[key] = value
return value return value
} }
// SetState updates a value in account storage. // SetState updates a value in account storage.
func (self *stateObject) SetState(db Database, key, value common.Hash) { func (self *stateObject) SetState(db Database, key, value common.Hash) {
// If the new value is the same as old, don't set
prev := self.GetState(db, key)
if prev == value {
return
}
// New value is different, update and journal the change
self.db.journal.append(storageChange{ self.db.journal.append(storageChange{
account: &self.address, account: &self.address,
key: key, key: key,
prevalue: self.GetState(db, key), prevalue: prev,
}) })
self.setState(key, value) self.setState(key, value)
} }
func (self *stateObject) setState(key, value common.Hash) { func (self *stateObject) setState(key, value common.Hash) {
self.cachedStorage[key] = value
self.dirtyStorage[key] = value self.dirtyStorage[key] = value
} }
@ -202,6 +219,13 @@ func (self *stateObject) updateTrie(db Database) Trie {
tr := self.getTrie(db) tr := self.getTrie(db)
for key, value := range self.dirtyStorage { for key, value := range self.dirtyStorage {
delete(self.dirtyStorage, key) delete(self.dirtyStorage, key)
// Skip noop changes, persist actual changes
if value == self.originStorage[key] {
continue
}
self.originStorage[key] = value
if (value == common.Hash{}) { if (value == common.Hash{}) {
self.setError(tr.TryDelete(key[:])) self.setError(tr.TryDelete(key[:]))
continue continue
@ -279,7 +303,7 @@ func (self *stateObject) deepCopy(db *StateDB) *stateObject {
} }
stateObject.code = self.code stateObject.code = self.code
stateObject.dirtyStorage = self.dirtyStorage.Copy() stateObject.dirtyStorage = self.dirtyStorage.Copy()
stateObject.cachedStorage = self.dirtyStorage.Copy() stateObject.originStorage = self.originStorage.Copy()
stateObject.suicided = self.suicided stateObject.suicided = self.suicided
stateObject.dirtyCode = self.dirtyCode stateObject.dirtyCode = self.dirtyCode
stateObject.deleted = self.deleted stateObject.deleted = self.deleted

View file

@ -96,11 +96,15 @@ func (s *StateSuite) TestNull(c *checker.C) {
s.state.CreateAccount(address) s.state.CreateAccount(address)
//value := common.FromHex("0x823140710bf13990e4500136726d8b55") //value := common.FromHex("0x823140710bf13990e4500136726d8b55")
var value common.Hash var value common.Hash
s.state.SetState(address, common.Hash{}, value) s.state.SetState(address, common.Hash{}, value)
s.state.Commit(false) s.state.Commit(false)
value = s.state.GetState(address, common.Hash{})
if value != (common.Hash{}) { if value := s.state.GetState(address, common.Hash{}); value != (common.Hash{}) {
c.Errorf("expected empty hash. got %x", value) c.Errorf("expected empty current value, got %x", value)
}
if value := s.state.GetCommittedState(address, common.Hash{}); value != (common.Hash{}) {
c.Errorf("expected empty committed value, got %x", value)
} }
} }
@ -110,20 +114,24 @@ func (s *StateSuite) TestSnapshot(c *checker.C) {
data1 := common.BytesToHash([]byte{42}) data1 := common.BytesToHash([]byte{42})
data2 := common.BytesToHash([]byte{43}) data2 := common.BytesToHash([]byte{43})
// snapshot the genesis state
genesis := s.state.Snapshot()
// set initial state object value // set initial state object value
s.state.SetState(stateobjaddr, storageaddr, data1) s.state.SetState(stateobjaddr, storageaddr, data1)
// get snapshot of current state
snapshot := s.state.Snapshot() snapshot := s.state.Snapshot()
// set new state object value // set a new state object value, revert it and ensure correct content
s.state.SetState(stateobjaddr, storageaddr, data2) s.state.SetState(stateobjaddr, storageaddr, data2)
// restore snapshot
s.state.RevertToSnapshot(snapshot) s.state.RevertToSnapshot(snapshot)
// get state storage value c.Assert(s.state.GetState(stateobjaddr, storageaddr), checker.DeepEquals, data1)
res := s.state.GetState(stateobjaddr, storageaddr) c.Assert(s.state.GetCommittedState(stateobjaddr, storageaddr), checker.DeepEquals, common.Hash{})
c.Assert(data1, checker.DeepEquals, res) // revert up to the genesis state and ensure correct content
s.state.RevertToSnapshot(genesis)
c.Assert(s.state.GetState(stateobjaddr, storageaddr), checker.DeepEquals, common.Hash{})
c.Assert(s.state.GetCommittedState(stateobjaddr, storageaddr), checker.DeepEquals, common.Hash{})
} }
func (s *StateSuite) TestSnapshotEmpty(c *checker.C) { func (s *StateSuite) TestSnapshotEmpty(c *checker.C) {
@ -208,24 +216,30 @@ func compareStateObjects(so0, so1 *stateObject, t *testing.T) {
t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code) t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code)
} }
if len(so1.cachedStorage) != len(so0.cachedStorage) { if len(so1.dirtyStorage) != len(so0.dirtyStorage) {
t.Errorf("Storage size mismatch: have %d, want %d", len(so1.cachedStorage), len(so0.cachedStorage)) t.Errorf("Dirty storage size mismatch: have %d, want %d", len(so1.dirtyStorage), len(so0.dirtyStorage))
} }
for k, v := range so1.cachedStorage { for k, v := range so1.dirtyStorage {
if so0.cachedStorage[k] != v { if so0.dirtyStorage[k] != v {
t.Errorf("Storage key %x mismatch: have %v, want %v", k, so0.cachedStorage[k], v) t.Errorf("Dirty storage key %x mismatch: have %v, want %v", k, so0.dirtyStorage[k], v)
} }
} }
for k, v := range so0.cachedStorage { for k, v := range so0.dirtyStorage {
if so1.cachedStorage[k] != v { if so1.dirtyStorage[k] != v {
t.Errorf("Storage key %x mismatch: have %v, want none.", k, v) t.Errorf("Dirty storage key %x mismatch: have %v, want none.", k, v)
} }
} }
if len(so1.originStorage) != len(so0.originStorage) {
if so0.suicided != so1.suicided { t.Errorf("Origin storage size mismatch: have %d, want %d", len(so1.originStorage), len(so0.originStorage))
t.Fatalf("suicided mismatch: have %v, want %v", so0.suicided, so1.suicided) }
for k, v := range so1.originStorage {
if so0.originStorage[k] != v {
t.Errorf("Origin storage key %x mismatch: have %v, want %v", k, so0.originStorage[k], v)
}
}
for k, v := range so0.originStorage {
if so1.originStorage[k] != v {
t.Errorf("Origin storage key %x mismatch: have %v, want none.", k, v)
} }
if so0.deleted != so1.deleted {
t.Fatalf("Deleted mismatch: have %v, want %v", so0.deleted, so1.deleted)
} }
} }

View file

@ -169,11 +169,22 @@ func (self *StateDB) Preimages() map[common.Hash][]byte {
return self.preimages return self.preimages
} }
// AddRefund adds gas to the refund counter
func (self *StateDB) AddRefund(gas uint64) { func (self *StateDB) AddRefund(gas uint64) {
self.journal.append(refundChange{prev: self.refund}) self.journal.append(refundChange{prev: self.refund})
self.refund += gas self.refund += gas
} }
// SubRefund removes gas from the refund counter.
// This method will panic if the refund counter goes below zero
func (self *StateDB) SubRefund(gas uint64) {
self.journal.append(refundChange{prev: self.refund})
if gas > self.refund {
panic("Refund counter below zero")
}
self.refund -= gas
}
// Exist reports whether the given account address exists in the state. // Exist reports whether the given account address exists in the state.
// Notably this also returns true for suicided accounts. // Notably this also returns true for suicided accounts.
func (self *StateDB) Exist(addr common.Address) bool { func (self *StateDB) Exist(addr common.Address) bool {
@ -236,10 +247,20 @@ func (self *StateDB) GetCodeHash(addr common.Address) common.Hash {
return common.BytesToHash(stateObject.CodeHash()) return common.BytesToHash(stateObject.CodeHash())
} }
func (self *StateDB) GetState(addr common.Address, bhash common.Hash) common.Hash { // GetState retrieves a value from the given account's storage trie.
func (self *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
stateObject := self.getStateObject(addr) stateObject := self.getStateObject(addr)
if stateObject != nil { if stateObject != nil {
return stateObject.GetState(self.db, bhash) return stateObject.GetState(self.db, hash)
}
return common.Hash{}
}
// GetCommittedState retrieves a value from the given account's committed storage trie.
func (self *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
stateObject := self.getStateObject(addr)
if stateObject != nil {
return stateObject.GetCommittedState(self.db, hash)
} }
return common.Hash{} return common.Hash{}
} }
@ -435,19 +456,14 @@ func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common
if so == nil { if so == nil {
return return
} }
// When iterating over the storage check the cache first
for h, value := range so.cachedStorage {
cb(h, value)
}
it := trie.NewIterator(so.getTrie(db.db).NodeIterator(nil)) it := trie.NewIterator(so.getTrie(db.db).NodeIterator(nil))
for it.Next() { for it.Next() {
// ignore cached values
key := common.BytesToHash(db.trie.GetKey(it.Key)) key := common.BytesToHash(db.trie.GetKey(it.Key))
if _, ok := so.cachedStorage[key]; !ok { if value, dirty := so.dirtyStorage[key]; dirty {
cb(key, common.BytesToHash(it.Value)) cb(key, value)
continue
} }
cb(key, common.BytesToHash(it.Value))
} }
} }

View file

@ -381,11 +381,11 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr)) checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr))
// Check storage. // Check storage.
if obj := state.getStateObject(addr); obj != nil { if obj := state.getStateObject(addr); obj != nil {
state.ForEachStorage(addr, func(key, val common.Hash) bool { state.ForEachStorage(addr, func(key, value common.Hash) bool {
return checkeq("GetState("+key.Hex()+")", val, checkstate.GetState(addr, key)) return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value)
}) })
checkstate.ForEachStorage(addr, func(key, checkval common.Hash) bool { checkstate.ForEachStorage(addr, func(key, value common.Hash) bool {
return checkeq("GetState("+key.Hex()+")", state.GetState(addr, key), checkval) return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value)
}) })
} }
if err != nil { if err != nil {

View file

@ -110,7 +110,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
*usedGas += gas *usedGas += gas
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
// based on the eip phase, we're passing wether the root touch-delete accounts. // based on the eip phase, we're passing whether the root touch-delete accounts.
receipt := types.NewReceipt(root, failed, *usedGas) receipt := types.NewReceipt(root, failed, *usedGas)
receipt.TxHash = tx.Hash() receipt.TxHash = tx.Hash()
receipt.GasUsed = gas receipt.GasUsed = gas

View file

@ -26,13 +26,13 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/prque"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
) )
const ( const (
@ -525,7 +525,7 @@ func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common
return pending, queued return pending, queued
} }
// Pending retrieves all currently processable transactions, groupped by origin // Pending retrieves all currently processable transactions, grouped by origin
// account and sorted by nonce. The returned transaction set is a copy and can be // account and sorted by nonce. The returned transaction set is a copy and can be
// freely modified by calling code. // freely modified by calling code.
func (pool *TxPool) Pending() (map[common.Address]types.Transactions, error) { func (pool *TxPool) Pending() (map[common.Address]types.Transactions, error) {
@ -547,7 +547,7 @@ func (pool *TxPool) Locals() []common.Address {
return pool.locals.flatten() return pool.locals.flatten()
} }
// local retrieves all currently known local transactions, groupped by origin // local retrieves all currently known local transactions, grouped by origin
// account and sorted by nonce. The returned transaction set is a copy and can be // account and sorted by nonce. The returned transaction set is a copy and can be
// freely modified by calling code. // freely modified by calling code.
func (pool *TxPool) local() map[common.Address]types.Transactions { func (pool *TxPool) local() map[common.Address]types.Transactions {
@ -987,11 +987,11 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) {
if pending > pool.config.GlobalSlots { if pending > pool.config.GlobalSlots {
pendingBeforeCap := pending pendingBeforeCap := pending
// Assemble a spam order to penalize large transactors first // Assemble a spam order to penalize large transactors first
spammers := prque.New() spammers := prque.New(nil)
for addr, list := range pool.pending { for addr, list := range pool.pending {
// Only evict transactions from high rollers // Only evict transactions from high rollers
if !pool.locals.contains(addr) && uint64(list.Len()) > pool.config.AccountSlots { if !pool.locals.contains(addr) && uint64(list.Len()) > pool.config.AccountSlots {
spammers.Push(addr, float32(list.Len())) spammers.Push(addr, int64(list.Len()))
} }
} }
// Gradually drop transactions from offenders // Gradually drop transactions from offenders

View file

@ -113,7 +113,7 @@ func LogsBloom(logs []*Log) *big.Int {
} }
func bloom9(b []byte) *big.Int { func bloom9(b []byte) *big.Int {
b = crypto.Keccak256(b[:]) b = crypto.Keccak256(b)
r := new(big.Int) r := new(big.Int)
@ -130,7 +130,7 @@ var Bloom9 = bloom9
func BloomLookup(bin Bloom, topic bytesBacked) bool { func BloomLookup(bin Bloom, topic bytesBacked) bool {
bloom := bin.Big() bloom := bin.Big()
cmp := bloom9(topic.Bytes()[:]) cmp := bloom9(topic.Bytes())
return bloom.And(bloom, cmp).Cmp(cmp) == 0 return bloom.And(bloom, cmp).Cmp(cmp) == 0
} }

View file

@ -41,7 +41,7 @@ type (
) )
// run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter. // run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
func run(evm *EVM, contract *Contract, input []byte) ([]byte, error) { func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) {
if contract.CodeAddr != nil { if contract.CodeAddr != nil {
precompiles := PrecompiledContractsHomestead precompiles := PrecompiledContractsHomestead
if evm.ChainConfig().IsByzantium(evm.BlockNumber) { if evm.ChainConfig().IsByzantium(evm.BlockNumber) {
@ -61,7 +61,7 @@ func run(evm *EVM, contract *Contract, input []byte) ([]byte, error) {
}(evm.interpreter) }(evm.interpreter)
evm.interpreter = interpreter evm.interpreter = interpreter
} }
return interpreter.Run(contract, input) return interpreter.Run(contract, input, readOnly)
} }
} }
return nil, ErrNoCompatibleInterpreter return nil, ErrNoCompatibleInterpreter
@ -136,10 +136,28 @@ func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmCon
vmConfig: vmConfig, vmConfig: vmConfig,
chainConfig: chainConfig, chainConfig: chainConfig,
chainRules: chainConfig.Rules(ctx.BlockNumber), chainRules: chainConfig.Rules(ctx.BlockNumber),
interpreters: make([]Interpreter, 1), interpreters: make([]Interpreter, 0, 1),
} }
evm.interpreters[0] = NewEVMInterpreter(evm, vmConfig) if chainConfig.IsEWASM(ctx.BlockNumber) {
// to be implemented by EVM-C and Wagon PRs.
// if vmConfig.EWASMInterpreter != "" {
// extIntOpts := strings.Split(vmConfig.EWASMInterpreter, ":")
// path := extIntOpts[0]
// options := []string{}
// if len(extIntOpts) > 1 {
// options = extIntOpts[1..]
// }
// evm.interpreters = append(evm.interpreters, NewEVMVCInterpreter(evm, vmConfig, options))
// } else {
// evm.interpreters = append(evm.interpreters, NewEWASMInterpreter(evm, vmConfig))
// }
panic("No supported ewasm interpreter yet.")
}
// vmConfig.EVMInterpreter will be used by EVM-C, it won't be checked here
// as we always want to have the built-in EVM as the failover option.
evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig))
evm.interpreter = evm.interpreters[0] evm.interpreter = evm.interpreters[0]
return evm return evm
@ -210,7 +228,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err) evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
}() }()
} }
ret, err = run(evm, contract, input) ret, err = run(evm, contract, input, false)
// When an error was returned by the EVM or when setting the creation code // When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally // above we revert to the snapshot and consume any gas remaining. Additionally
@ -255,7 +273,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
contract := NewContract(caller, to, value, gas) contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
ret, err = run(evm, contract, input) ret, err = run(evm, contract, input, false)
if err != nil { if err != nil {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted { if err != errExecutionReverted {
@ -288,7 +306,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
contract := NewContract(caller, to, nil, gas).AsDelegate() contract := NewContract(caller, to, nil, gas).AsDelegate()
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
ret, err = run(evm, contract, input) ret, err = run(evm, contract, input, false)
if err != nil { if err != nil {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted { if err != errExecutionReverted {
@ -310,13 +328,6 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
if evm.depth > int(params.CallCreateDepth) { if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth return nil, gas, ErrDepth
} }
// Make sure the readonly is only set if we aren't in readonly yet
// this makes also sure that the readonly flag isn't removed for
// child calls.
if !evm.interpreter.IsReadOnly() {
evm.interpreter.SetReadOnly(true)
defer func() { evm.interpreter.SetReadOnly(false) }()
}
var ( var (
to = AccountRef(addr) to = AccountRef(addr)
@ -331,7 +342,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// When an error was returned by the EVM or when setting the creation code // When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally // above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in Homestead this also counts for code storage gas errors. // when we're in Homestead this also counts for code storage gas errors.
ret, err = run(evm, contract, input) ret, err = run(evm, contract, input, true)
if err != nil { if err != nil {
evm.StateDB.RevertToSnapshot(snapshot) evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted { if err != errExecutionReverted {
@ -382,7 +393,7 @@ func (evm *EVM) create(caller ContractRef, code []byte, gas uint64, value *big.I
} }
start := time.Now() start := time.Now()
ret, err := run(evm, contract, nil) ret, err := run(evm, contract, nil, false)
// check whether the max code size has been exceeded // check whether the max code size has been exceeded
maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize

View file

@ -118,24 +118,69 @@ func gasReturnDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *
func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var ( var (
y, x = stack.Back(1), stack.Back(0) y, x = stack.Back(1), stack.Back(0)
val = evm.StateDB.GetState(contract.Address(), common.BigToHash(x)) current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
) )
// This checks for 3 scenario's and calculates gas accordingly // The legacy gas metering only takes into consideration the current state
if !evm.chainRules.IsConstantinople {
// This checks for 3 scenario's and calculates gas accordingly:
//
// 1. From a zero-value address to a non-zero value (NEW VALUE) // 1. From a zero-value address to a non-zero value (NEW VALUE)
// 2. From a non-zero value address to a zero-value address (DELETE) // 2. From a non-zero value address to a zero-value address (DELETE)
// 3. From a non-zero to a non-zero (CHANGE) // 3. From a non-zero to a non-zero (CHANGE)
if val == (common.Hash{}) && y.Sign() != 0 { switch {
// 0 => non 0 case current == (common.Hash{}) && y.Sign() != 0: // 0 => non 0
return params.SstoreSetGas, nil return params.SstoreSetGas, nil
} else if val != (common.Hash{}) && y.Sign() == 0 { case current != (common.Hash{}) && y.Sign() == 0: // non 0 => 0
// non 0 => 0
evm.StateDB.AddRefund(params.SstoreRefundGas) evm.StateDB.AddRefund(params.SstoreRefundGas)
return params.SstoreClearGas, nil return params.SstoreClearGas, nil
} else { default: // non 0 => non 0 (or 0 => 0)
// non 0 => non 0 (or 0 => 0)
return params.SstoreResetGas, nil return params.SstoreResetGas, nil
} }
} }
// The new gas metering is based on net gas costs (EIP-1283):
//
// 1. If current value equals new value (this is a no-op), 200 gas is deducted.
// 2. If current value does not equal new value
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context)
// 2.1.1. If original value is 0, 20000 gas is deducted.
// 2.1.2. Otherwise, 5000 gas is deducted. If new value is 0, add 15000 gas to refund counter.
// 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses.
// 2.2.1. If original value is not 0
// 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0.
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter.
// 2.2.2. If original value equals new value (this storage slot is reset)
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
value := common.BigToHash(y)
if current == value { // noop (1)
return params.NetSstoreNoopGas, nil
}
original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x))
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return params.NetSstoreInitGas, nil
}
if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(params.NetSstoreClearRefund)
}
return params.NetSstoreCleanGas, nil // write existing slot (2.1.2)
}
if original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
evm.StateDB.SubRefund(params.NetSstoreClearRefund)
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
evm.StateDB.AddRefund(params.NetSstoreClearRefund)
}
}
if original == value {
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
evm.StateDB.AddRefund(params.NetSstoreResetClearRefund)
} else { // reset to original existing slot (2.2.2.2)
evm.StateDB.AddRefund(params.NetSstoreResetRefund)
}
}
return params.NetSstoreDirtyGas, nil
}
func makeGasLog(n uint64) gasFunc { func makeGasLog(n uint64) gasFunc {
return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { return func(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {

View file

@ -355,7 +355,7 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *
defer interpreter.intPool.put(shift) // First operand back into the pool defer interpreter.intPool.put(shift) // First operand back into the pool
if shift.Cmp(common.Big256) >= 0 { if shift.Cmp(common.Big256) >= 0 {
if value.Sign() > 0 { if value.Sign() >= 0 {
value.SetUint64(0) value.SetUint64(0)
} else { } else {
value.SetInt64(-1) value.SetInt64(-1)
@ -727,7 +727,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo
} }
func opCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { func opCall(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
// Pop gas. The actual gas in in interpreter.evm.callGasTemp. // Pop gas. The actual gas in interpreter.evm.callGasTemp.
interpreter.intPool.put(stack.pop()) interpreter.intPool.put(stack.pop())
gas := interpreter.evm.callGasTemp gas := interpreter.evm.callGasTemp
// Pop other call parameters. // Pop other call parameters.

View file

@ -40,8 +40,10 @@ type StateDB interface {
GetCodeSize(common.Address) int GetCodeSize(common.Address) int
AddRefund(uint64) AddRefund(uint64)
SubRefund(uint64)
GetRefund() uint64 GetRefund() uint64
GetCommittedState(common.Address, common.Hash) common.Hash
GetState(common.Address, common.Hash) common.Hash GetState(common.Address, common.Hash) common.Hash
SetState(common.Address, common.Hash, common.Hash) SetState(common.Address, common.Hash, common.Hash)
@ -64,7 +66,7 @@ type StateDB interface {
ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) ForEachStorage(common.Address, func(common.Hash, common.Hash) bool)
} }
// CallContext provides a basic interface for the EVM calling conventions. The EVM EVM // CallContext provides a basic interface for the EVM calling conventions. The EVM
// depends on this context being implemented for doing subcalls and initialising new EVM contracts. // depends on this context being implemented for doing subcalls and initialising new EVM contracts.
type CallContext interface { type CallContext interface {
// Call another contract // Call another contract

View file

@ -39,6 +39,11 @@ type Config struct {
// may be left uninitialised and will be set to the default // may be left uninitialised and will be set to the default
// table. // table.
JumpTable [256]operation JumpTable [256]operation
// Type of the EWASM interpreter
EWASMInterpreter string
// Type of the EVM interpreter
EVMInterpreter string
} }
// Interpreter is used to run Ethereum based contracts and will utilise the // Interpreter is used to run Ethereum based contracts and will utilise the
@ -48,7 +53,7 @@ type Config struct {
type Interpreter interface { type Interpreter interface {
// Run loops and evaluates the contract's code with the given input data and returns // Run loops and evaluates the contract's code with the given input data and returns
// the return byte-slice and an error if one occurred. // the return byte-slice and an error if one occurred.
Run(contract *Contract, input []byte) ([]byte, error) Run(contract *Contract, input []byte, static bool) ([]byte, error)
// CanRun tells if the contract, passed as an argument, can be // CanRun tells if the contract, passed as an argument, can be
// run by the current interpreter. This is meant so that the // run by the current interpreter. This is meant so that the
// caller can do something like: // caller can do something like:
@ -61,10 +66,6 @@ type Interpreter interface {
// } // }
// ``` // ```
CanRun([]byte) bool CanRun([]byte) bool
// IsReadOnly reports if the interpreter is in read only mode.
IsReadOnly() bool
// SetReadOnly sets (or unsets) read only mode in the interpreter.
SetReadOnly(bool)
} }
// EVMInterpreter represents an EVM interpreter // EVMInterpreter represents an EVM interpreter
@ -125,7 +126,7 @@ func (in *EVMInterpreter) enforceRestrictions(op OpCode, operation operation, st
// It's important to note that any errors returned by the interpreter should be // It's important to note that any errors returned by the interpreter should be
// considered a revert-and-consume-all-gas operation except for // considered a revert-and-consume-all-gas operation except for
// errExecutionReverted which means revert-and-keep-gas-left. // errExecutionReverted which means revert-and-keep-gas-left.
func (in *EVMInterpreter) Run(contract *Contract, input []byte) (ret []byte, err error) { func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
if in.intPool == nil { if in.intPool == nil {
in.intPool = poolOfIntPools.get() in.intPool = poolOfIntPools.get()
defer func() { defer func() {
@ -138,6 +139,13 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte) (ret []byte, err
in.evm.depth++ in.evm.depth++
defer func() { in.evm.depth-- }() defer func() { in.evm.depth-- }()
// Make sure the readOnly is only set if we aren't in readOnly yet.
// This makes also sure that the readOnly flag isn't removed for child calls.
if readOnly && !in.readOnly {
in.readOnly = true
defer func() { in.readOnly = false }()
}
// Reset the previous call's return data. It's unimportant to preserve the old buffer // Reset the previous call's return data. It's unimportant to preserve the old buffer
// as every returning call will return new data anyway. // as every returning call will return new data anyway.
in.returnData = nil in.returnData = nil
@ -263,13 +271,3 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte) (ret []byte, err
func (in *EVMInterpreter) CanRun(code []byte) bool { func (in *EVMInterpreter) CanRun(code []byte) bool {
return true return true
} }
// IsReadOnly reports if the interpreter is in read only mode.
func (in *EVMInterpreter) IsReadOnly() bool {
return in.readOnly
}
// SetReadOnly sets (or unsets) read only mode in the interpreter.
func (in *EVMInterpreter) SetReadOnly(ro bool) {
in.readOnly = ro
}

View file

@ -41,11 +41,6 @@ func (d *dummyContractRef) SetBalance(*big.Int) {}
func (d *dummyContractRef) SetNonce(uint64) {} func (d *dummyContractRef) SetNonce(uint64) {}
func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) } func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) }
type dummyStateDB struct {
NoopStateDB
ref *dummyContractRef
}
func TestStoreCapture(t *testing.T) { func TestStoreCapture(t *testing.T) {
var ( var (
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})

View file

@ -29,7 +29,7 @@ type Memory struct {
lastGasCost uint64 lastGasCost uint64
} }
// NewMemory returns a new memory memory model. // NewMemory returns a new memory model.
func NewMemory() *Memory { func NewMemory() *Memory {
return &Memory{} return &Memory{}
} }

View file

@ -1,70 +0,0 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package vm
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
func NoopCanTransfer(db StateDB, from common.Address, balance *big.Int) bool {
return true
}
func NoopTransfer(db StateDB, from, to common.Address, amount *big.Int) {}
type NoopEVMCallContext struct{}
func (NoopEVMCallContext) Call(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
return nil, nil
}
func (NoopEVMCallContext) CallCode(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
return nil, nil
}
func (NoopEVMCallContext) Create(caller ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
return nil, common.Address{}, nil
}
func (NoopEVMCallContext) DelegateCall(me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
return nil, nil
}
type NoopStateDB struct{}
func (NoopStateDB) CreateAccount(common.Address) {}
func (NoopStateDB) SubBalance(common.Address, *big.Int) {}
func (NoopStateDB) AddBalance(common.Address, *big.Int) {}
func (NoopStateDB) GetBalance(common.Address) *big.Int { return nil }
func (NoopStateDB) GetNonce(common.Address) uint64 { return 0 }
func (NoopStateDB) SetNonce(common.Address, uint64) {}
func (NoopStateDB) GetCodeHash(common.Address) common.Hash { return common.Hash{} }
func (NoopStateDB) GetCode(common.Address) []byte { return nil }
func (NoopStateDB) SetCode(common.Address, []byte) {}
func (NoopStateDB) GetCodeSize(common.Address) int { return 0 }
func (NoopStateDB) AddRefund(uint64) {}
func (NoopStateDB) GetRefund() uint64 { return 0 }
func (NoopStateDB) GetState(common.Address, common.Hash) common.Hash { return common.Hash{} }
func (NoopStateDB) SetState(common.Address, common.Hash, common.Hash) {}
func (NoopStateDB) Suicide(common.Address) bool { return false }
func (NoopStateDB) HasSuicided(common.Address) bool { return false }
func (NoopStateDB) Exist(common.Address) bool { return false }
func (NoopStateDB) Empty(common.Address) bool { return false }
func (NoopStateDB) RevertToSnapshot(int) {}
func (NoopStateDB) Snapshot() int { return 0 }
func (NoopStateDB) AddLog(*types.Log) {}
func (NoopStateDB) AddPreimage(common.Hash, []byte) {}
func (NoopStateDB) ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) {}

View file

@ -54,7 +54,7 @@ static void secp256k1_num_mul(secp256k1_num *r, const secp256k1_num *a, const se
even if r was negative. */ even if r was negative. */
static void secp256k1_num_mod(secp256k1_num *r, const secp256k1_num *m); static void secp256k1_num_mod(secp256k1_num *r, const secp256k1_num *m);
/** Right-shift the passed number by bits bits. */ /** Right-shift the passed number by bits. */
static void secp256k1_num_shift(secp256k1_num *r, int bits); static void secp256k1_num_shift(secp256k1_num *r, int bits);
/** Check whether a number is zero. */ /** Check whether a number is zero. */

View file

@ -26,7 +26,6 @@
} while(0) } while(0)
static void default_illegal_callback_fn(const char* str, void* data) { static void default_illegal_callback_fn(const char* str, void* data) {
(void)data;
fprintf(stderr, "[libsecp256k1] illegal argument: %s\n", str); fprintf(stderr, "[libsecp256k1] illegal argument: %s\n", str);
abort(); abort();
} }
@ -37,7 +36,6 @@ static const secp256k1_callback default_illegal_callback = {
}; };
static void default_error_callback_fn(const char* str, void* data) { static void default_error_callback_fn(const char* str, void* data) {
(void)data;
fprintf(stderr, "[libsecp256k1] internal consistency check failed: %s\n", str); fprintf(stderr, "[libsecp256k1] internal consistency check failed: %s\n", str);
abort(); abort();
} }

View file

@ -127,7 +127,7 @@ func (api *PrivateDebugAPI) TraceChain(ctx context.Context, start, end rpc.Block
// traceChain configures a new tracer according to the provided configuration, and // traceChain configures a new tracer according to the provided configuration, and
// executes all the transactions contained within. The return value will be one item // executes all the transactions contained within. The return value will be one item
// per transaction, dependent on the requestd tracer. // per transaction, dependent on the requested tracer.
func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Block, config *TraceConfig) (*rpc.Subscription, error) { func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Block, config *TraceConfig) (*rpc.Subscription, error) {
// Tracing a chain is a **long** operation, only do with subscriptions // Tracing a chain is a **long** operation, only do with subscriptions
notifier, supported := rpc.NotifierFromContext(ctx) notifier, supported := rpc.NotifierFromContext(ctx)

View file

@ -149,10 +149,14 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion) rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion)
} }
var ( var (
vmConfig = vm.Config{EnablePreimageRecording: config.EnablePreimageRecording} vmConfig = vm.Config{
EnablePreimageRecording: config.EnablePreimageRecording,
EWASMInterpreter: config.EWASMInterpreter,
EVMInterpreter: config.EVMInterpreter,
}
cacheConfig = &core.CacheConfig{Disabled: config.NoPruning, TrieNodeLimit: config.TrieCache, TrieTimeLimit: config.TrieTimeout} cacheConfig = &core.CacheConfig{Disabled: config.NoPruning, TrieNodeLimit: config.TrieCache, TrieTimeLimit: config.TrieTimeout}
) )
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, eth.chainConfig, eth.engine, vmConfig) eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, eth.chainConfig, eth.engine, vmConfig, eth.shouldPreserve)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -173,7 +177,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
return nil, err return nil, err
} }
eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine, config.MinerRecommit, config.MinerGasFloor, config.MinerGasCeil) eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine, config.MinerRecommit, config.MinerGasFloor, config.MinerGasCeil, eth.isLocalBlock)
eth.miner.SetExtra(makeExtraData(config.MinerExtraData)) eth.miner.SetExtra(makeExtraData(config.MinerExtraData))
eth.APIBackend = &EthAPIBackend{eth, nil} eth.APIBackend = &EthAPIBackend{eth, nil}
@ -330,6 +334,60 @@ func (s *Ethereum) Etherbase() (eb common.Address, err error) {
return common.Address{}, fmt.Errorf("etherbase must be explicitly specified") return common.Address{}, fmt.Errorf("etherbase must be explicitly specified")
} }
// isLocalBlock checks whether the specified block is mined
// by local miner accounts.
//
// We regard two types of accounts as local miner account: etherbase
// and accounts specified via `txpool.locals` flag.
func (s *Ethereum) isLocalBlock(block *types.Block) bool {
author, err := s.engine.Author(block.Header())
if err != nil {
log.Warn("Failed to retrieve block author", "number", block.NumberU64(), "hash", block.Hash(), "err", err)
return false
}
// Check whether the given address is etherbase.
s.lock.RLock()
etherbase := s.etherbase
s.lock.RUnlock()
if author == etherbase {
return true
}
// Check whether the given address is specified by `txpool.local`
// CLI flag.
for _, account := range s.config.TxPool.Locals {
if account == author {
return true
}
}
return false
}
// shouldPreserve checks whether we should preserve the given block
// during the chain reorg depending on whether the author of block
// is a local account.
func (s *Ethereum) shouldPreserve(block *types.Block) bool {
// The reason we need to disable the self-reorg preserving for clique
// is it can be probable to introduce a deadlock.
//
// e.g. If there are 7 available signers
//
// r1 A
// r2 B
// r3 C
// r4 D
// r5 A [X] F G
// r6 [X]
//
// In the round5, the inturn signer E is offline, so the worst case
// is A, F and G sign the block of round5 and reject the block of opponents
// and in the round6, the last available signer B is offline, the whole
// network is stuck.
if _, ok := s.engine.(*clique.Clique); ok {
return false
}
return s.isLocalBlock(block)
}
// SetEtherbase sets the mining reward address. // SetEtherbase sets the mining reward address.
func (s *Ethereum) SetEtherbase(etherbase common.Address) { func (s *Ethereum) SetEtherbase(etherbase common.Address) {
s.lock.Lock() s.lock.Lock()
@ -362,7 +420,7 @@ func (s *Ethereum) StartMining(threads int) error {
s.lock.RUnlock() s.lock.RUnlock()
s.txPool.SetGasPrice(price) s.txPool.SetGasPrice(price)
// Configure the local mining addess // Configure the local mining address
eb, err := s.Etherbase() eb, err := s.Etherbase()
if err != nil { if err != nil {
log.Error("Cannot start mining without etherbase", "err", err) log.Error("Cannot start mining without etherbase", "err", err)

View file

@ -124,6 +124,11 @@ type Config struct {
// specify time to exit after syncing // specify time to exit after syncing
ExitWhenSynced time.Duration ExitWhenSynced time.Duration
// Type of the EWASM interpreter ("" for detault)
EWASMInterpreter string
// Type of the EVM interpreter ("" for default)
EVMInterpreter string
} }
type configMarshaling struct { type configMarshaling struct {

View file

@ -26,10 +26,10 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/prque"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
) )
var ( var (
@ -105,11 +105,11 @@ func newQueue() *queue {
headerPendPool: make(map[string]*fetchRequest), headerPendPool: make(map[string]*fetchRequest),
headerContCh: make(chan bool), headerContCh: make(chan bool),
blockTaskPool: make(map[common.Hash]*types.Header), blockTaskPool: make(map[common.Hash]*types.Header),
blockTaskQueue: prque.New(), blockTaskQueue: prque.New(nil),
blockPendPool: make(map[string]*fetchRequest), blockPendPool: make(map[string]*fetchRequest),
blockDonePool: make(map[common.Hash]struct{}), blockDonePool: make(map[common.Hash]struct{}),
receiptTaskPool: make(map[common.Hash]*types.Header), receiptTaskPool: make(map[common.Hash]*types.Header),
receiptTaskQueue: prque.New(), receiptTaskQueue: prque.New(nil),
receiptPendPool: make(map[string]*fetchRequest), receiptPendPool: make(map[string]*fetchRequest),
receiptDonePool: make(map[common.Hash]struct{}), receiptDonePool: make(map[common.Hash]struct{}),
resultCache: make([]*fetchResult, blockCacheItems), resultCache: make([]*fetchResult, blockCacheItems),
@ -277,7 +277,7 @@ func (q *queue) ScheduleSkeleton(from uint64, skeleton []*types.Header) {
} }
// Schedule all the header retrieval tasks for the skeleton assembly // Schedule all the header retrieval tasks for the skeleton assembly
q.headerTaskPool = make(map[uint64]*types.Header) q.headerTaskPool = make(map[uint64]*types.Header)
q.headerTaskQueue = prque.New() q.headerTaskQueue = prque.New(nil)
q.headerPeerMiss = make(map[string]map[uint64]struct{}) // Reset availability to correct invalid chains q.headerPeerMiss = make(map[string]map[uint64]struct{}) // Reset availability to correct invalid chains
q.headerResults = make([]*types.Header, len(skeleton)*MaxHeaderFetch) q.headerResults = make([]*types.Header, len(skeleton)*MaxHeaderFetch)
q.headerProced = 0 q.headerProced = 0
@ -288,7 +288,7 @@ func (q *queue) ScheduleSkeleton(from uint64, skeleton []*types.Header) {
index := from + uint64(i*MaxHeaderFetch) index := from + uint64(i*MaxHeaderFetch)
q.headerTaskPool[index] = header q.headerTaskPool[index] = header
q.headerTaskQueue.Push(index, -float32(index)) q.headerTaskQueue.Push(index, -int64(index))
} }
} }
@ -334,11 +334,11 @@ func (q *queue) Schedule(headers []*types.Header, from uint64) []*types.Header {
} }
// Queue the header for content retrieval // Queue the header for content retrieval
q.blockTaskPool[hash] = header q.blockTaskPool[hash] = header
q.blockTaskQueue.Push(header, -float32(header.Number.Uint64())) q.blockTaskQueue.Push(header, -int64(header.Number.Uint64()))
if q.mode == FastSync { if q.mode == FastSync {
q.receiptTaskPool[hash] = header q.receiptTaskPool[hash] = header
q.receiptTaskQueue.Push(header, -float32(header.Number.Uint64())) q.receiptTaskQueue.Push(header, -int64(header.Number.Uint64()))
} }
inserts = append(inserts, header) inserts = append(inserts, header)
q.headerHead = hash q.headerHead = hash
@ -436,7 +436,7 @@ func (q *queue) ReserveHeaders(p *peerConnection, count int) *fetchRequest {
} }
// Merge all the skipped batches back // Merge all the skipped batches back
for _, from := range skip { for _, from := range skip {
q.headerTaskQueue.Push(from, -float32(from)) q.headerTaskQueue.Push(from, -int64(from))
} }
// Assemble and return the block download request // Assemble and return the block download request
if send == 0 { if send == 0 {
@ -542,7 +542,7 @@ func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common
} }
// Merge all the skipped headers back // Merge all the skipped headers back
for _, header := range skip { for _, header := range skip {
taskQueue.Push(header, -float32(header.Number.Uint64())) taskQueue.Push(header, -int64(header.Number.Uint64()))
} }
if progress { if progress {
// Wake WaitResults, resultCache was modified // Wake WaitResults, resultCache was modified
@ -585,10 +585,10 @@ func (q *queue) cancel(request *fetchRequest, taskQueue *prque.Prque, pendPool m
defer q.lock.Unlock() defer q.lock.Unlock()
if request.From > 0 { if request.From > 0 {
taskQueue.Push(request.From, -float32(request.From)) taskQueue.Push(request.From, -int64(request.From))
} }
for _, header := range request.Headers { for _, header := range request.Headers {
taskQueue.Push(header, -float32(header.Number.Uint64())) taskQueue.Push(header, -int64(header.Number.Uint64()))
} }
delete(pendPool, request.Peer.id) delete(pendPool, request.Peer.id)
} }
@ -602,13 +602,13 @@ func (q *queue) Revoke(peerID string) {
if request, ok := q.blockPendPool[peerID]; ok { if request, ok := q.blockPendPool[peerID]; ok {
for _, header := range request.Headers { for _, header := range request.Headers {
q.blockTaskQueue.Push(header, -float32(header.Number.Uint64())) q.blockTaskQueue.Push(header, -int64(header.Number.Uint64()))
} }
delete(q.blockPendPool, peerID) delete(q.blockPendPool, peerID)
} }
if request, ok := q.receiptPendPool[peerID]; ok { if request, ok := q.receiptPendPool[peerID]; ok {
for _, header := range request.Headers { for _, header := range request.Headers {
q.receiptTaskQueue.Push(header, -float32(header.Number.Uint64())) q.receiptTaskQueue.Push(header, -int64(header.Number.Uint64()))
} }
delete(q.receiptPendPool, peerID) delete(q.receiptPendPool, peerID)
} }
@ -657,10 +657,10 @@ func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest,
// Return any non satisfied requests to the pool // Return any non satisfied requests to the pool
if request.From > 0 { if request.From > 0 {
taskQueue.Push(request.From, -float32(request.From)) taskQueue.Push(request.From, -int64(request.From))
} }
for _, header := range request.Headers { for _, header := range request.Headers {
taskQueue.Push(header, -float32(header.Number.Uint64())) taskQueue.Push(header, -int64(header.Number.Uint64()))
} }
// Add the peer to the expiry report along the number of failed requests // Add the peer to the expiry report along the number of failed requests
expiries[id] = len(request.Headers) expiries[id] = len(request.Headers)
@ -731,7 +731,7 @@ func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh
} }
miss[request.From] = struct{}{} miss[request.From] = struct{}{}
q.headerTaskQueue.Push(request.From, -float32(request.From)) q.headerTaskQueue.Push(request.From, -int64(request.From))
return 0, errors.New("delivery not accepted") return 0, errors.New("delivery not accepted")
} }
// Clean up a successful fetch and try to deliver any sub-results // Clean up a successful fetch and try to deliver any sub-results
@ -854,7 +854,7 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQ
// Return all failed or missing fetches to the queue // Return all failed or missing fetches to the queue
for _, header := range request.Headers { for _, header := range request.Headers {
if header != nil { if header != nil {
taskQueue.Push(header, -float32(header.Number.Uint64())) taskQueue.Push(header, -int64(header.Number.Uint64()))
} }
} }
// Wake up WaitResults // Wake up WaitResults

View file

@ -23,10 +23,10 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/prque"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
) )
const ( const (
@ -160,7 +160,7 @@ func New(getBlock blockRetrievalFn, verifyHeader headerVerifierFn, broadcastBloc
fetching: make(map[common.Hash]*announce), fetching: make(map[common.Hash]*announce),
fetched: make(map[common.Hash][]*announce), fetched: make(map[common.Hash][]*announce),
completing: make(map[common.Hash]*announce), completing: make(map[common.Hash]*announce),
queue: prque.New(), queue: prque.New(nil),
queues: make(map[string]int), queues: make(map[string]int),
queued: make(map[common.Hash]*inject), queued: make(map[common.Hash]*inject),
getBlock: getBlock, getBlock: getBlock,
@ -299,7 +299,7 @@ func (f *Fetcher) loop() {
// If too high up the chain or phase, continue later // If too high up the chain or phase, continue later
number := op.block.NumberU64() number := op.block.NumberU64()
if number > height+1 { if number > height+1 {
f.queue.Push(op, -float32(number)) f.queue.Push(op, -int64(number))
if f.queueChangeHook != nil { if f.queueChangeHook != nil {
f.queueChangeHook(hash, true) f.queueChangeHook(hash, true)
} }
@ -624,7 +624,7 @@ func (f *Fetcher) enqueue(peer string, block *types.Block) {
} }
f.queues[peer] = count f.queues[peer] = count
f.queued[hash] = op f.queued[hash] = op
f.queue.Push(op, -float32(block.NumberU64())) f.queue.Push(op, -int64(block.NumberU64()))
if f.queueChangeHook != nil { if f.queueChangeHook != nil {
f.queueChangeHook(op.block.Hash(), true) f.queueChangeHook(op.block.Hash(), true)
} }

View file

@ -37,7 +37,7 @@ import (
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
@ -147,7 +147,7 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
NodeInfo: func() interface{} { NodeInfo: func() interface{} {
return manager.NodeInfo() return manager.NodeInfo()
}, },
PeerInfo: func(id discover.NodeID) interface{} { PeerInfo: func(id enode.ID) interface{} {
if p := manager.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil { if p := manager.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil {
return p.Info() return p.Info()
} }

View file

@ -472,7 +472,7 @@ func testDAOChallenge(t *testing.T, localForked, remoteForked bool, timeout bool
config = &params.ChainConfig{DAOForkBlock: big.NewInt(1), DAOForkSupport: localForked} config = &params.ChainConfig{DAOForkBlock: big.NewInt(1), DAOForkSupport: localForked}
gspec = &core.Genesis{Config: config} gspec = &core.Genesis{Config: config}
genesis = gspec.MustCommit(db) genesis = gspec.MustCommit(db)
blockchain, _ = core.NewBlockChain(db, nil, config, pow, vm.Config{}) blockchain, _ = core.NewBlockChain(db, nil, config, pow, vm.Config{}, nil)
) )
pm, err := NewProtocolManager(config, downloader.FullSync, DefaultConfig.NetworkId, evmux, new(testTxPool), pow, blockchain, db) pm, err := NewProtocolManager(config, downloader.FullSync, DefaultConfig.NetworkId, evmux, new(testTxPool), pow, blockchain, db)
if err != nil { if err != nil {

View file

@ -37,7 +37,7 @@ import (
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
@ -59,7 +59,7 @@ func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func
Alloc: core.GenesisAlloc{testBank: {Balance: big.NewInt(1000000)}}, Alloc: core.GenesisAlloc{testBank: {Balance: big.NewInt(1000000)}},
} }
genesis = gspec.MustCommit(db) genesis = gspec.MustCommit(db)
blockchain, _ = core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}) blockchain, _ = core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil)
) )
chain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, blocks, generator) chain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, blocks, generator)
if _, err := blockchain.InsertChain(chain); err != nil { if _, err := blockchain.InsertChain(chain); err != nil {
@ -148,7 +148,7 @@ func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*te
app, net := p2p.MsgPipe() app, net := p2p.MsgPipe()
// Generate a random id and create the peer // Generate a random id and create the peer
var id discover.NodeID var id enode.ID
rand.Read(id[:]) rand.Read(id[:])
peer := pm.newPeer(version, p2p.NewPeer(id, name, nil), net) peer := pm.newPeer(version, p2p.NewPeer(id, name, nil), net)

View file

@ -25,7 +25,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/enode"
) )
const ( const (
@ -64,7 +64,7 @@ func (pm *ProtocolManager) syncTransactions(p *peer) {
// the transactions in small packs to one peer at a time. // the transactions in small packs to one peer at a time.
func (pm *ProtocolManager) txsyncLoop() { func (pm *ProtocolManager) txsyncLoop() {
var ( var (
pending = make(map[discover.NodeID]*txsync) pending = make(map[enode.ID]*txsync)
sending = false // whether a send is active sending = false // whether a send is active
pack = new(txsync) // the pack that is being sent pack = new(txsync) // the pack that is being sent
done = make(chan error, 1) // result of the send done = make(chan error, 1) // result of the send

View file

@ -23,7 +23,7 @@ import (
"github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/enode"
) )
// Tests that fast sync gets disabled as soon as a real block is successfully // Tests that fast sync gets disabled as soon as a real block is successfully
@ -42,8 +42,8 @@ func TestFastSyncDisabling(t *testing.T) {
// Sync up the two peers // Sync up the two peers
io1, io2 := p2p.MsgPipe() io1, io2 := p2p.MsgPipe()
go pmFull.handle(pmFull.newPeer(63, p2p.NewPeer(discover.NodeID{}, "empty", nil), io2)) go pmFull.handle(pmFull.newPeer(63, p2p.NewPeer(enode.ID{}, "empty", nil), io2))
go pmEmpty.handle(pmEmpty.newPeer(63, p2p.NewPeer(discover.NodeID{}, "full", nil), io1)) go pmEmpty.handle(pmEmpty.newPeer(63, p2p.NewPeer(enode.ID{}, "full", nil), io1))
time.Sleep(250 * time.Millisecond) time.Sleep(250 * time.Millisecond)
pmEmpty.synchronise(pmEmpty.peers.BestPeer()) pmEmpty.synchronise(pmEmpty.peers.BestPeer())

View file

@ -124,7 +124,7 @@ func (mw *memoryWrapper) pushObject(vm *duktape.Context) {
ctx.Pop2() ctx.Pop2()
ptr := ctx.PushFixedBuffer(len(blob)) ptr := ctx.PushFixedBuffer(len(blob))
copy(makeSlice(ptr, uint(len(blob))), blob[:]) copy(makeSlice(ptr, uint(len(blob))), blob)
return 1 return 1
}) })
vm.PutPropString(obj, "slice") vm.PutPropString(obj, "slice")
@ -204,7 +204,7 @@ func (dw *dbWrapper) pushObject(vm *duktape.Context) {
code := dw.db.GetCode(common.BytesToAddress(popSlice(ctx))) code := dw.db.GetCode(common.BytesToAddress(popSlice(ctx)))
ptr := ctx.PushFixedBuffer(len(code)) ptr := ctx.PushFixedBuffer(len(code))
copy(makeSlice(ptr, uint(len(code))), code[:]) copy(makeSlice(ptr, uint(len(code))), code)
return 1 return 1
}) })
vm.PutPropString(obj, "getCode") vm.PutPropString(obj, "getCode")
@ -268,7 +268,7 @@ func (cw *contractWrapper) pushObject(vm *duktape.Context) {
blob := cw.contract.Input blob := cw.contract.Input
ptr := ctx.PushFixedBuffer(len(blob)) ptr := ctx.PushFixedBuffer(len(blob))
copy(makeSlice(ptr, uint(len(blob))), blob[:]) copy(makeSlice(ptr, uint(len(blob))), blob)
return 1 return 1
}) })
vm.PutPropString(obj, "getInput") vm.PutPropString(obj, "getInput")
@ -584,7 +584,7 @@ func (jst *Tracer) GetResult() (json.RawMessage, error) {
case []byte: case []byte:
ptr := jst.vm.PushFixedBuffer(len(val)) ptr := jst.vm.PushFixedBuffer(len(val))
copy(makeSlice(ptr, uint(len(val))), val[:]) copy(makeSlice(ptr, uint(len(val))), val)
case common.Address: case common.Address:
ptr := jst.vm.PushFixedBuffer(20) ptr := jst.vm.PushFixedBuffer(20)

View file

@ -49,7 +49,7 @@ func runTrace(tracer *Tracer) (json.RawMessage, error) {
contract := vm.NewContract(account{}, account{}, big.NewInt(0), 10000) contract := vm.NewContract(account{}, account{}, big.NewInt(0), 10000)
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
_, err := env.Interpreter().Run(contract, []byte{}) _, err := env.Interpreter().Run(contract, []byte{}, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -155,15 +155,12 @@ func (db *LDBDatabase) LDB() *leveldb.DB {
// Meter configures the database metrics collectors and // Meter configures the database metrics collectors and
func (db *LDBDatabase) Meter(prefix string) { func (db *LDBDatabase) Meter(prefix string) {
if metrics.Enabled {
// Initialize all the metrics collector at the requested prefix // Initialize all the metrics collector at the requested prefix
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.diskReadMeter = metrics.NewRegisteredMeter(prefix+"disk/read", nil)
db.diskWriteMeter = metrics.NewRegisteredMeter(prefix+"disk/write", nil) db.diskWriteMeter = metrics.NewRegisteredMeter(prefix+"disk/write", nil)
}
// Initialize write delay metrics no matter we are in metric mode or not.
db.writeDelayMeter = metrics.NewRegisteredMeter(prefix+"compact/writedelay/duration", nil) db.writeDelayMeter = metrics.NewRegisteredMeter(prefix+"compact/writedelay/duration", nil)
db.writeDelayNMeter = metrics.NewRegisteredMeter(prefix+"compact/writedelay/counter", nil) db.writeDelayNMeter = metrics.NewRegisteredMeter(prefix+"compact/writedelay/counter", nil)

View file

@ -143,9 +143,9 @@ func CopyFile(dst, src string, mode os.FileMode) {
// so that go commands executed by build use the same version of Go as the 'host' that runs // so that go commands executed by build use the same version of Go as the 'host' that runs
// build code. e.g. // build code. e.g.
// //
// /usr/lib/go-1.8/bin/go run build/ci.go ... // /usr/lib/go-1.11/bin/go run build/ci.go ...
// //
// runs using go 1.8 and invokes go 1.8 tools from the same GOROOT. This is also important // runs using go 1.11 and invokes go 1.11 tools from the same GOROOT. This is also important
// because runtime.Version checks on the host should match the tools that are run. // because runtime.Version checks on the host should match the tools that are run.
func GoTool(tool string, args ...string) *exec.Cmd { func GoTool(tool string, args ...string) *exec.Cmd {
args = append([]string{tool}, args...) args = append([]string{tool}, args...)

View file

@ -449,7 +449,7 @@ func (s *PrivateAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr c
// addr = ecrecover(hash, signature) // addr = ecrecover(hash, signature)
// //
// Note, the signature must conform to the secp256k1 curve R, S and V values, where // Note, the signature must conform to the secp256k1 curve R, S and V values, where
// the V value must be be 27 or 28 for legacy reasons. // the V value must be 27 or 28 for legacy reasons.
// //
// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover // https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover
func (s *PrivateAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) { func (s *PrivateAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) {

View file

@ -1021,7 +1021,7 @@ var formatOutputInt = function (param) {
var value = param.staticPart() || "0"; var value = param.staticPart() || "0";
// check if it's negative number // check if it's negative number
// it it is, return two's complement // it is, return two's complement
if (signedIsNegative(value)) { if (signedIsNegative(value)) {
return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1); return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1);
} }
@ -2250,7 +2250,7 @@ var isAddress = function (address) {
// check if it has the basic requirements of an address // check if it has the basic requirements of an address
return false; return false;
} else if (/^(0x)?[0-9a-f]{40}$/.test(address) || /^(0x)?[0-9A-F]{40}$/.test(address)) { } else if (/^(0x)?[0-9a-f]{40}$/.test(address) || /^(0x)?[0-9A-F]{40}$/.test(address)) {
// If it's all small caps or all all caps, return true // If it's all small caps or all caps, return true
return true; return true;
} else { } else {
// Otherwise check each case // Otherwise check each case

View file

@ -26,7 +26,7 @@ import (
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/light"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
@ -47,7 +47,7 @@ type NodeInfo struct {
Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block
Config *params.ChainConfig `json:"config"` // Chain configuration for the fork rules Config *params.ChainConfig `json:"config"` // Chain configuration for the fork rules
Head common.Hash `json:"head"` // SHA3 hash of the host's best owned block Head common.Hash `json:"head"` // SHA3 hash of the host's best owned block
CHT light.TrustedCheckpoint `json:"cht"` // Trused CHT checkpoint for fast catchup CHT params.TrustedCheckpoint `json:"cht"` // Trused CHT checkpoint for fast catchup
} }
// makeProtocols creates protocol descriptors for the given LES versions. // makeProtocols creates protocol descriptors for the given LES versions.
@ -63,7 +63,7 @@ func (c *lesCommons) makeProtocols(versions []uint) []p2p.Protocol {
Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
return c.protocolManager.runPeer(version, p, rw) return c.protocolManager.runPeer(version, p, rw)
}, },
PeerInfo: func(id discover.NodeID) interface{} { PeerInfo: func(id enode.ID) interface{} {
if p := c.protocolManager.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil { if p := c.protocolManager.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil {
return p.Info() return p.Info()
} }
@ -76,7 +76,7 @@ func (c *lesCommons) makeProtocols(versions []uint) []p2p.Protocol {
// nodeInfo retrieves some protocol metadata about the running host node. // nodeInfo retrieves some protocol metadata about the running host node.
func (c *lesCommons) nodeInfo() interface{} { func (c *lesCommons) nodeInfo() interface{} {
var cht light.TrustedCheckpoint var cht params.TrustedCheckpoint
sections, _, _ := c.chtIndexer.Sections() sections, _, _ := c.chtIndexer.Sections()
sections2, _, _ := c.bloomTrieIndexer.Sections() sections2, _, _ := c.bloomTrieIndexer.Sections()
@ -98,8 +98,8 @@ func (c *lesCommons) nodeInfo() interface{} {
idxV2 := (sectionIndex+1)*c.iConfig.PairChtSize/c.iConfig.ChtSize - 1 idxV2 := (sectionIndex+1)*c.iConfig.PairChtSize/c.iConfig.ChtSize - 1
chtRoot = light.GetChtRoot(c.chainDb, idxV2, sectionHead) chtRoot = light.GetChtRoot(c.chainDb, idxV2, sectionHead)
} }
cht = light.TrustedCheckpoint{ cht = params.TrustedCheckpoint{
SectionIdx: sectionIndex, SectionIndex: sectionIndex,
SectionHead: sectionHead, SectionHead: sectionHead,
CHTRoot: chtRoot, CHTRoot: chtRoot,
BloomRoot: light.GetBloomTrieRoot(c.chainDb, sectionIndex, sectionHead), BloomRoot: light.GetBloomTrieRoot(c.chainDb, sectionIndex, sectionHead),

View file

@ -114,7 +114,9 @@ func (d *requestDistributor) loop() {
d.lock.Lock() d.lock.Lock()
elem := d.reqQueue.Front() elem := d.reqQueue.Front()
for elem != nil { for elem != nil {
close(elem.Value.(*distReq).sentChn) req := elem.Value.(*distReq)
close(req.sentChn)
req.sentChn = nil
elem = elem.Next() elem = elem.Next()
} }
d.lock.Unlock() d.lock.Unlock()

View file

@ -213,8 +213,7 @@ func (pm *ProtocolManager) runPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWrit
var entry *poolEntry var entry *poolEntry
peer := pm.newPeer(int(version), pm.networkId, p, rw) peer := pm.newPeer(int(version), pm.networkId, p, rw)
if pm.serverPool != nil { if pm.serverPool != nil {
addr := p.RemoteAddr().(*net.TCPAddr) entry = pm.serverPool.connect(peer, peer.Node())
entry = pm.serverPool.connect(peer, addr.IP, uint16(addr.Port))
} }
peer.poolEntry = entry peer.poolEntry = entry
select { select {
@ -382,7 +381,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
} }
if p.requestAnnounceType == announceTypeSigned { if p.requestAnnounceType == announceTypeSigned {
if err := req.checkSignature(p.pubKey); err != nil { if err := req.checkSignature(p.ID()); err != nil {
p.Log().Trace("Invalid announcement signature", "err", err) p.Log().Trace("Invalid announcement signature", "err", err)
return err return err
} }

View file

@ -38,7 +38,7 @@ import (
"github.com/ethereum/go-ethereum/les/flowcontrol" "github.com/ethereum/go-ethereum/les/flowcontrol"
"github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/light"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
@ -164,7 +164,7 @@ func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *cor
if lightSync { if lightSync {
chain, _ = light.NewLightChain(odr, gspec.Config, engine) chain, _ = light.NewLightChain(odr, gspec.Config, engine)
} else { } else {
blockchain, _ := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}) blockchain, _ := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil)
gchain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, blocks, generator) gchain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, blocks, generator)
if _, err := blockchain.InsertChain(gchain); err != nil { if _, err := blockchain.InsertChain(gchain); err != nil {
panic(err) panic(err)
@ -221,7 +221,7 @@ func newTestPeer(t *testing.T, name string, version int, pm *ProtocolManager, sh
app, net := p2p.MsgPipe() app, net := p2p.MsgPipe()
// Generate a random id and create the peer // Generate a random id and create the peer
var id discover.NodeID var id enode.ID
rand.Read(id[:]) rand.Read(id[:])
peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net) peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net)
@ -258,7 +258,7 @@ func newTestPeerPair(name string, version int, pm, pm2 *ProtocolManager) (*peer,
app, net := p2p.MsgPipe() app, net := p2p.MsgPipe()
// Generate a random id and create the peer // Generate a random id and create the peer
var id discover.NodeID var id enode.ID
rand.Read(id[:]) rand.Read(id[:])
peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net) peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net)

View file

@ -478,7 +478,7 @@ func (r *ChtRequest) Validate(db ethdb.Database, msg *Msg) error {
} }
type BloomReq struct { type BloomReq struct {
BloomTrieNum, BitIdx, SectionIdx, FromLevel uint64 BloomTrieNum, BitIdx, SectionIndex, FromLevel uint64
} }
// ODR request type for requesting headers by Canonical Hash Trie, see LesOdrRequest interface // ODR request type for requesting headers by Canonical Hash Trie, see LesOdrRequest interface
@ -487,7 +487,7 @@ type BloomRequest light.BloomRequest
// GetCost returns the cost of the given ODR request according to the serving // GetCost returns the cost of the given ODR request according to the serving
// peer's cost table (implementation of LesOdrRequest) // peer's cost table (implementation of LesOdrRequest)
func (r *BloomRequest) GetCost(peer *peer) uint64 { func (r *BloomRequest) GetCost(peer *peer) uint64 {
return peer.GetRequestCost(GetHelperTrieProofsMsg, len(r.SectionIdxList)) return peer.GetRequestCost(GetHelperTrieProofsMsg, len(r.SectionIndexList))
} }
// CanSend tells if a certain peer is suitable for serving the given request // CanSend tells if a certain peer is suitable for serving the given request
@ -503,13 +503,13 @@ func (r *BloomRequest) CanSend(peer *peer) bool {
// Request sends an ODR request to the LES network (implementation of LesOdrRequest) // Request sends an ODR request to the LES network (implementation of LesOdrRequest)
func (r *BloomRequest) Request(reqID uint64, peer *peer) error { func (r *BloomRequest) Request(reqID uint64, peer *peer) error {
peer.Log().Debug("Requesting BloomBits", "bloomTrie", r.BloomTrieNum, "bitIdx", r.BitIdx, "sections", r.SectionIdxList) peer.Log().Debug("Requesting BloomBits", "bloomTrie", r.BloomTrieNum, "bitIdx", r.BitIdx, "sections", r.SectionIndexList)
reqs := make([]HelperTrieReq, len(r.SectionIdxList)) reqs := make([]HelperTrieReq, len(r.SectionIndexList))
var encNumber [10]byte var encNumber [10]byte
binary.BigEndian.PutUint16(encNumber[:2], uint16(r.BitIdx)) binary.BigEndian.PutUint16(encNumber[:2], uint16(r.BitIdx))
for i, sectionIdx := range r.SectionIdxList { for i, sectionIdx := range r.SectionIndexList {
binary.BigEndian.PutUint64(encNumber[2:], sectionIdx) binary.BigEndian.PutUint64(encNumber[2:], sectionIdx)
reqs[i] = HelperTrieReq{ reqs[i] = HelperTrieReq{
Type: htBloomBits, Type: htBloomBits,
@ -524,7 +524,7 @@ func (r *BloomRequest) Request(reqID uint64, peer *peer) error {
// returns true and stores results in memory if the message was a valid reply // returns true and stores results in memory if the message was a valid reply
// to the request (implementation of LesOdrRequest) // to the request (implementation of LesOdrRequest)
func (r *BloomRequest) Validate(db ethdb.Database, msg *Msg) error { func (r *BloomRequest) Validate(db ethdb.Database, msg *Msg) error {
log.Debug("Validating BloomBits", "bloomTrie", r.BloomTrieNum, "bitIdx", r.BitIdx, "sections", r.SectionIdxList) log.Debug("Validating BloomBits", "bloomTrie", r.BloomTrieNum, "bitIdx", r.BitIdx, "sections", r.SectionIndexList)
// Ensure we have a correct message with a single proof element // Ensure we have a correct message with a single proof element
if msg.MsgType != MsgHelperTrieProofs { if msg.MsgType != MsgHelperTrieProofs {
@ -535,13 +535,13 @@ func (r *BloomRequest) Validate(db ethdb.Database, msg *Msg) error {
nodeSet := proofs.NodeSet() nodeSet := proofs.NodeSet()
reads := &readTraceDB{db: nodeSet} reads := &readTraceDB{db: nodeSet}
r.BloomBits = make([][]byte, len(r.SectionIdxList)) r.BloomBits = make([][]byte, len(r.SectionIndexList))
// Verify the proofs // Verify the proofs
var encNumber [10]byte var encNumber [10]byte
binary.BigEndian.PutUint16(encNumber[:2], uint16(r.BitIdx)) binary.BigEndian.PutUint16(encNumber[:2], uint16(r.BitIdx))
for i, idx := range r.SectionIdxList { for i, idx := range r.SectionIndexList {
binary.BigEndian.PutUint64(encNumber[2:], idx) binary.BigEndian.PutUint64(encNumber[2:], idx)
value, _, err := trie.VerifyProof(r.BloomTrieRoot, encNumber[:], reads) value, _, err := trie.VerifyProof(r.BloomTrieRoot, encNumber[:], reads)
if err != nil { if err != nil {

View file

@ -18,7 +18,6 @@
package les package les
import ( import (
"crypto/ecdsa"
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
@ -51,7 +50,6 @@ const (
type peer struct { type peer struct {
*p2p.Peer *p2p.Peer
pubKey *ecdsa.PublicKey
rw p2p.MsgReadWriter rw p2p.MsgReadWriter
@ -80,11 +78,9 @@ type peer struct {
func newPeer(version int, network uint64, p *p2p.Peer, rw p2p.MsgReadWriter) *peer { func newPeer(version int, network uint64, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
id := p.ID() id := p.ID()
pubKey, _ := id.Pubkey()
return &peer{ return &peer{
Peer: p, Peer: p,
pubKey: pubKey,
rw: rw, rw: rw,
version: version, version: version,
network: network, network: network,

View file

@ -18,9 +18,7 @@
package les package les
import ( import (
"bytes"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -30,7 +28,7 @@ import (
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
@ -148,21 +146,20 @@ func (a *announceData) sign(privKey *ecdsa.PrivateKey) {
} }
// checkSignature verifies if the block announcement has a valid signature by the given pubKey // checkSignature verifies if the block announcement has a valid signature by the given pubKey
func (a *announceData) checkSignature(pubKey *ecdsa.PublicKey) error { func (a *announceData) checkSignature(id enode.ID) error {
var sig []byte var sig []byte
if err := a.Update.decode().get("sign", &sig); err != nil { if err := a.Update.decode().get("sign", &sig); err != nil {
return err return err
} }
rlp, _ := rlp.EncodeToBytes(announceBlock{a.Hash, a.Number, a.Td}) rlp, _ := rlp.EncodeToBytes(announceBlock{a.Hash, a.Number, a.Td})
recPubkey, err := secp256k1.RecoverPubkey(crypto.Keccak256(rlp), sig) recPubkey, err := crypto.SigToPub(crypto.Keccak256(rlp), sig)
if err != nil { if err != nil {
return err return err
} }
pbytes := elliptic.Marshal(pubKey.Curve, pubKey.X, pubKey.Y) if id == enode.PubkeyToIDV4(recPubkey) {
if bytes.Equal(pbytes, recPubkey) {
return nil return nil
} }
return errors.New("Wrong signature") return errors.New("wrong signature")
} }
type blockInfo struct { type blockInfo struct {

View file

@ -217,6 +217,13 @@ func (r *sentReq) stateRequesting() reqStateFn {
go r.tryRequest() go r.tryRequest()
r.lastReqQueued = true r.lastReqQueued = true
return r.stateRequesting return r.stateRequesting
case rpDeliveredInvalid:
// if it was the last sent request (set to nil by update) then start a new one
if !r.lastReqQueued && r.lastReqSentTo == nil {
go r.tryRequest()
r.lastReqQueued = true
}
return r.stateRequesting
case rpDeliveredValid: case rpDeliveredValid:
r.stop(nil) r.stop(nil)
return r.stateStopped return r.stateStopped
@ -242,7 +249,11 @@ func (r *sentReq) stateNoMorePeers() reqStateFn {
r.stop(nil) r.stop(nil)
return r.stateStopped return r.stateStopped
} }
if r.waiting() {
return r.stateNoMorePeers return r.stateNoMorePeers
}
r.stop(light.ErrNoPeers)
return nil
case <-r.stopCh: case <-r.stopCh:
return r.stateStopped return r.stateStopped
} }

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