mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
Merge branch 'master' into master
This commit is contained in:
commit
c9daec2cbe
315 changed files with 9244 additions and 6857 deletions
46
.github/CONTRIBUTING.md
vendored
46
.github/CONTRIBUTING.md
vendored
|
|
@ -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.
|
|
||||||
|
|
|
||||||
4
.github/no-response.yml
vendored
4
.github/no-response.yml
vendored
|
|
@ -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.
|
||||||
|
|
|
||||||
29
.travis.yml
29
.travis.yml
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
[](https://goreportcard.com/report/github.com/ethereum/go-ethereum)
|
[](https://goreportcard.com/report/github.com/ethereum/go-ethereum)
|
||||||
[](https://travis-ci.org/ethereum/go-ethereum)
|
[](https://travis-ci.org/ethereum/go-ethereum)
|
||||||
[](https://gitter.im/ethereum/go-ethereum?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
[](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/.
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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{
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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 != "" {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 + "/")
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
func (r *testerChainReader) Config() *params.ChainConfig { return params.AllCliqueProtocolChanges }
|
|
||||||
func (r *testerChainReader) CurrentHeader() *types.Header { panic("not supported") }
|
|
||||||
func (r *testerChainReader) GetHeader(common.Hash, uint64) *types.Header { panic("not supported") }
|
|
||||||
func (r *testerChainReader) GetBlock(common.Hash, uint64) *types.Block { panic("not supported") }
|
|
||||||
func (r *testerChainReader) GetHeaderByHash(common.Hash) *types.Header { panic("not supported") }
|
|
||||||
func (r *testerChainReader) GetHeaderByNumber(number uint64) *types.Header {
|
|
||||||
if number == 0 {
|
|
||||||
return rawdb.ReadHeader(r.db, rawdb.ReadCanonicalHash(r.db, 0), 0)
|
|
||||||
}
|
}
|
||||||
return nil
|
// 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that voting is evaluated correctly for various simple and complex scenarios.
|
// testerVote represents a single block signed by a parcitular account, where
|
||||||
func TestVoting(t *testing.T) {
|
// the account may or may not have cast a Clique vote.
|
||||||
|
type testerVote struct {
|
||||||
|
signer string
|
||||||
|
voted string
|
||||||
|
auth bool
|
||||||
|
checkpoint []string
|
||||||
|
newbatch bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that Clique signer voting is evaluated correctly for various simple and
|
||||||
|
// 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 = ¶ms.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(¶ms.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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -376,6 +394,7 @@ func calcDifficultyByzantium(time uint64, parent *types.Header) *big.Int {
|
||||||
x.Add(x, y)
|
x.Add(x, y)
|
||||||
}
|
}
|
||||||
return x
|
return x
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// calcDifficultyHomestead is the difficulty adjustment algorithm. It returns
|
// calcDifficultyHomestead is the difficulty adjustment algorithm. It returns
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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}...)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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()))
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -118,23 +118,68 @@ 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 {
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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{})
|
||||||
|
|
|
||||||
|
|
@ -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{}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) {}
|
|
||||||
|
|
@ -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. */
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -472,7 +472,7 @@ func testDAOChallenge(t *testing.T, localForked, remoteForked bool, timeout bool
|
||||||
config = ¶ms.ChainConfig{DAOForkBlock: big.NewInt(1), DAOForkSupport: localForked}
|
config = ¶ms.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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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...)
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
Loading…
Reference in a new issue