mirror of https://github.com/ethereum/go-ethereum
core, consensus: pluggable consensus engines (#3817)
This commit adds pluggable consensus engines to go-ethereum. In short, it introduces a generic consensus interface, and refactors the entire codebase to use this interface.pull/13866/head
parent
e50a5b7771
commit
09777952ee
@ -0,0 +1,94 @@ |
|||||||
|
// Copyright 2017 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 consensus implements different Ethereum consensus engines.
|
||||||
|
package consensus |
||||||
|
|
||||||
|
import ( |
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/core/state" |
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
"github.com/ethereum/go-ethereum/params" |
||||||
|
"github.com/ethereum/go-ethereum/rpc" |
||||||
|
) |
||||||
|
|
||||||
|
// ChainReader defines a small collection of methods needed to access the local
|
||||||
|
// blockchain during header and/or uncle verification.
|
||||||
|
type ChainReader interface { |
||||||
|
// Config retrieves the blockchain's chain configuration.
|
||||||
|
Config() *params.ChainConfig |
||||||
|
|
||||||
|
// CurrentHeader retrieves the current header from the local chain.
|
||||||
|
CurrentHeader() *types.Header |
||||||
|
|
||||||
|
// GetHeader retrieves a block header from the database by hash and number.
|
||||||
|
GetHeader(hash common.Hash, number uint64) *types.Header |
||||||
|
|
||||||
|
// GetHeaderByNumber retrieves a block header from the database by number.
|
||||||
|
GetHeaderByNumber(number uint64) *types.Header |
||||||
|
|
||||||
|
// GetBlock retrieves a block from the database by hash and number.
|
||||||
|
GetBlock(hash common.Hash, number uint64) *types.Block |
||||||
|
} |
||||||
|
|
||||||
|
// Engine is an algorithm agnostic consensus engine.
|
||||||
|
type Engine interface { |
||||||
|
// VerifyHeader checks whether a header conforms to the consensus rules of a
|
||||||
|
// given engine. Verifying the seal may be done optionally here, or explicitly
|
||||||
|
// via the VerifySeal method.
|
||||||
|
VerifyHeader(chain ChainReader, header *types.Header, seal bool) error |
||||||
|
|
||||||
|
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
|
||||||
|
// concurrently. The method returns a quit channel to abort the operations and
|
||||||
|
// a results channel to retrieve the async verifications (the order is that of
|
||||||
|
// the input slice).
|
||||||
|
VerifyHeaders(chain ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) |
||||||
|
|
||||||
|
// VerifyUncles verifies that the given block's uncles conform to the consensus
|
||||||
|
// rules of a given engine.
|
||||||
|
VerifyUncles(chain ChainReader, block *types.Block) error |
||||||
|
|
||||||
|
// VerifySeal checks whether the crypto seal on a header is valid according to
|
||||||
|
// the consensus rules of the given engine.
|
||||||
|
VerifySeal(chain ChainReader, header *types.Header) error |
||||||
|
|
||||||
|
// Prepare initializes the consensus fields of a block header according to the
|
||||||
|
// rules of a particular engine. The changes are executed inline.
|
||||||
|
Prepare(chain ChainReader, header *types.Header) error |
||||||
|
|
||||||
|
// Finalize runs any post-transaction state modifications (e.g. block rewards)
|
||||||
|
// and assembles the final block.
|
||||||
|
//
|
||||||
|
// Note, the block header and state database might be updated to reflect any
|
||||||
|
// consensus rules that happen at finalization (e.g. block rewards).
|
||||||
|
Finalize(chain ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, |
||||||
|
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) |
||||||
|
|
||||||
|
// Seal generates a new block for the given input block with the local miner's
|
||||||
|
// seal place on top.
|
||||||
|
Seal(chain ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error) |
||||||
|
|
||||||
|
// APIs returns the RPC APIs this consensus engine provides.
|
||||||
|
APIs(chain ChainReader) []rpc.API |
||||||
|
} |
||||||
|
|
||||||
|
// PoW is a consensus engine based on proof-of-work.
|
||||||
|
type PoW interface { |
||||||
|
Engine |
||||||
|
|
||||||
|
// Hashrate returns the current mining hashrate of a PoW consensus engine.
|
||||||
|
Hashrate() float64 |
||||||
|
} |
@ -0,0 +1,496 @@ |
|||||||
|
// Copyright 2017 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 ethash |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
"math/big" |
||||||
|
"runtime" |
||||||
|
"sync/atomic" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/common/math" |
||||||
|
"github.com/ethereum/go-ethereum/consensus" |
||||||
|
"github.com/ethereum/go-ethereum/consensus/misc" |
||||||
|
"github.com/ethereum/go-ethereum/core/state" |
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
"github.com/ethereum/go-ethereum/params" |
||||||
|
set "gopkg.in/fatih/set.v0" |
||||||
|
) |
||||||
|
|
||||||
|
// Ethash proof-of-work protocol constants.
|
||||||
|
var ( |
||||||
|
blockReward *big.Int = big.NewInt(5e+18) // Block reward in wei for successfully mining a block
|
||||||
|
maxUncles = 2 // Maximum number of uncles allowed in a single block
|
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
ErrInvalidChain = errors.New("invalid header chain") |
||||||
|
ErrParentUnknown = errors.New("parent not known locally") |
||||||
|
ErrFutureBlock = errors.New("block in the future") |
||||||
|
ErrLargeBlockTimestamp = errors.New("timestamp too big") |
||||||
|
ErrZeroBlockTime = errors.New("timestamp equals parent's") |
||||||
|
ErrInvalidNumber = errors.New("invalid block number") |
||||||
|
ErrTooManyUncles = errors.New("too many uncles") |
||||||
|
ErrDuplicateUncle = errors.New("duplicate uncle") |
||||||
|
ErrUncleIsAncestor = errors.New("uncle is ancestor") |
||||||
|
ErrDanglingUncle = errors.New("uncle's parent is not ancestor") |
||||||
|
ErrNonceOutOfRange = errors.New("nonce out of range") |
||||||
|
ErrInvalidDifficulty = errors.New("non-positive difficulty") |
||||||
|
ErrInvalidMixDigest = errors.New("invalid mix digest") |
||||||
|
ErrInvalidPoW = errors.New("invalid proof-of-work") |
||||||
|
) |
||||||
|
|
||||||
|
// VerifyHeader checks whether a header conforms to the consensus rules of the
|
||||||
|
// stock Ethereum ethash engine.
|
||||||
|
func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error { |
||||||
|
// If we're running a full engine faking, accept any input as valid
|
||||||
|
if ethash.fakeFull { |
||||||
|
return nil |
||||||
|
} |
||||||
|
// Short circuit if the header is known, or it's parent not
|
||||||
|
number := header.Number.Uint64() |
||||||
|
if chain.GetHeader(header.Hash(), number) != nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
parent := chain.GetHeader(header.ParentHash, number-1) |
||||||
|
if parent == nil { |
||||||
|
return ErrParentUnknown |
||||||
|
} |
||||||
|
// Sanity checks passed, do a proper verification
|
||||||
|
return ethash.verifyHeader(chain, header, parent, false, seal) |
||||||
|
} |
||||||
|
|
||||||
|
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
|
||||||
|
// concurrently. The method returns a quit channel to abort the operations and
|
||||||
|
// a results channel to retrieve the async verifications.
|
||||||
|
func (ethash *Ethash) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { |
||||||
|
// If we're running a full engine faking, accept any input as valid
|
||||||
|
if ethash.fakeFull { |
||||||
|
abort, results := make(chan struct{}), make(chan error, len(headers)) |
||||||
|
for i := 0; i < len(headers); i++ { |
||||||
|
results <- nil |
||||||
|
} |
||||||
|
return abort, results |
||||||
|
} |
||||||
|
// Spawn as many workers as allowed threads
|
||||||
|
workers := runtime.GOMAXPROCS(0) |
||||||
|
if len(headers) < workers { |
||||||
|
workers = len(headers) |
||||||
|
} |
||||||
|
// Create a task channel and spawn the verifiers
|
||||||
|
type result struct { |
||||||
|
index int |
||||||
|
err error |
||||||
|
} |
||||||
|
inputs := make(chan int, workers) |
||||||
|
outputs := make(chan result, len(headers)) |
||||||
|
|
||||||
|
var badblock uint64 |
||||||
|
for i := 0; i < workers; i++ { |
||||||
|
go func() { |
||||||
|
for index := range inputs { |
||||||
|
// If we've found a bad block already before this, stop validating
|
||||||
|
if bad := atomic.LoadUint64(&badblock); bad != 0 && bad <= headers[index].Number.Uint64() { |
||||||
|
outputs <- result{index: index, err: ErrInvalidChain} |
||||||
|
continue |
||||||
|
} |
||||||
|
// We need to look up the first parent
|
||||||
|
var parent *types.Header |
||||||
|
if index == 0 { |
||||||
|
parent = chain.GetHeader(headers[0].ParentHash, headers[0].Number.Uint64()-1) |
||||||
|
} else if headers[index-1].Hash() == headers[index].ParentHash { |
||||||
|
parent = headers[index-1] |
||||||
|
} |
||||||
|
// Ensure the validation is useful and execute it
|
||||||
|
var failure error |
||||||
|
switch { |
||||||
|
case chain.GetHeader(headers[index].Hash(), headers[index].Number.Uint64()-1) != nil: |
||||||
|
outputs <- result{index: index, err: nil} |
||||||
|
case parent == nil: |
||||||
|
failure = ErrParentUnknown |
||||||
|
outputs <- result{index: index, err: failure} |
||||||
|
default: |
||||||
|
failure = ethash.verifyHeader(chain, headers[index], parent, false, seals[index]) |
||||||
|
outputs <- result{index: index, err: failure} |
||||||
|
} |
||||||
|
// If a validation failure occurred, mark subsequent blocks invalid
|
||||||
|
if failure != nil { |
||||||
|
number := headers[index].Number.Uint64() |
||||||
|
if prev := atomic.LoadUint64(&badblock); prev == 0 || prev > number { |
||||||
|
// This two step atomic op isn't thread-safe in that `badblock` might end
|
||||||
|
// up slightly higher than the block number of the first failure (if many
|
||||||
|
// workers try to write at the same time), but it's fine as we're mostly
|
||||||
|
// interested to avoid large useless work, we don't care about 1-2 extra
|
||||||
|
// runs. Doing "full thread safety" would involve mutexes, which would be
|
||||||
|
// a noticeable sync overhead on the fast spinning worker routines.
|
||||||
|
atomic.StoreUint64(&badblock, number) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}() |
||||||
|
} |
||||||
|
// Feed item indices to the workers until done, sorting and feeding the results to the caller
|
||||||
|
dones := make([]bool, len(headers)) |
||||||
|
errors := make([]error, len(headers)) |
||||||
|
|
||||||
|
abort := make(chan struct{}) |
||||||
|
returns := make(chan error, len(headers)) |
||||||
|
|
||||||
|
go func() { |
||||||
|
defer close(inputs) |
||||||
|
|
||||||
|
input, output := 0, 0 |
||||||
|
for i := 0; i < len(headers)*2; i++ { |
||||||
|
var res result |
||||||
|
|
||||||
|
// If there are tasks left, push to workers
|
||||||
|
if input < len(headers) { |
||||||
|
select { |
||||||
|
case inputs <- input: |
||||||
|
input++ |
||||||
|
continue |
||||||
|
case <-abort: |
||||||
|
return |
||||||
|
case res = <-outputs: |
||||||
|
} |
||||||
|
} else { |
||||||
|
// Otherwise keep waiting for results
|
||||||
|
select { |
||||||
|
case <-abort: |
||||||
|
return |
||||||
|
case res = <-outputs: |
||||||
|
} |
||||||
|
} |
||||||
|
// A result arrived, save and propagate if next
|
||||||
|
dones[res.index], errors[res.index] = true, res.err |
||||||
|
for output < len(headers) && dones[output] { |
||||||
|
returns <- errors[output] |
||||||
|
output++ |
||||||
|
} |
||||||
|
} |
||||||
|
}() |
||||||
|
return abort, returns |
||||||
|
} |
||||||
|
|
||||||
|
// VerifyUncles verifies that the given block's uncles conform to the consensus
|
||||||
|
// rules of the stock Ethereum ethash engine.
|
||||||
|
func (ethash *Ethash) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { |
||||||
|
// If we're running a full engine faking, accept any input as valid
|
||||||
|
if ethash.fakeFull { |
||||||
|
return nil |
||||||
|
} |
||||||
|
// Verify that there are at most 2 uncles included in this block
|
||||||
|
if len(block.Uncles()) > maxUncles { |
||||||
|
return ErrTooManyUncles |
||||||
|
} |
||||||
|
// Gather the set of past uncles and ancestors
|
||||||
|
uncles, ancestors := set.New(), make(map[common.Hash]*types.Header) |
||||||
|
|
||||||
|
number, parent := block.NumberU64()-1, block.ParentHash() |
||||||
|
for i := 0; i < 7; i++ { |
||||||
|
ancestor := chain.GetBlock(parent, number) |
||||||
|
if ancestor == nil { |
||||||
|
break |
||||||
|
} |
||||||
|
ancestors[ancestor.Hash()] = ancestor.Header() |
||||||
|
for _, uncle := range ancestor.Uncles() { |
||||||
|
uncles.Add(uncle.Hash()) |
||||||
|
} |
||||||
|
parent, number = ancestor.ParentHash(), number-1 |
||||||
|
} |
||||||
|
ancestors[block.Hash()] = block.Header() |
||||||
|
uncles.Add(block.Hash()) |
||||||
|
|
||||||
|
// Verify each of the uncles that it's recent, but not an ancestor
|
||||||
|
for _, uncle := range block.Uncles() { |
||||||
|
// Make sure every uncle is rewarded only once
|
||||||
|
hash := uncle.Hash() |
||||||
|
if uncles.Has(hash) { |
||||||
|
return ErrDuplicateUncle |
||||||
|
} |
||||||
|
uncles.Add(hash) |
||||||
|
|
||||||
|
// Make sure the uncle has a valid ancestry
|
||||||
|
if ancestors[hash] != nil { |
||||||
|
return ErrUncleIsAncestor |
||||||
|
} |
||||||
|
if ancestors[uncle.ParentHash] == nil || uncle.ParentHash == block.ParentHash() { |
||||||
|
return ErrDanglingUncle |
||||||
|
} |
||||||
|
if err := ethash.verifyHeader(chain, uncle, ancestors[uncle.ParentHash], true, true); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// verifyHeader checks whether a header conforms to the consensus rules of the
|
||||||
|
// stock Ethereum ethash engine.
|
||||||
|
//
|
||||||
|
// See YP section 4.3.4. "Block Header Validity"
|
||||||
|
func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *types.Header, uncle bool, seal bool) error { |
||||||
|
// Ensure that the header's extra-data section is of a reasonable size
|
||||||
|
if uint64(len(header.Extra)) > params.MaximumExtraDataSize { |
||||||
|
return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize) |
||||||
|
} |
||||||
|
// Verify the header's timestamp
|
||||||
|
if uncle { |
||||||
|
if header.Time.Cmp(math.MaxBig256) > 0 { |
||||||
|
return ErrLargeBlockTimestamp |
||||||
|
} |
||||||
|
} else { |
||||||
|
if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 { |
||||||
|
return ErrFutureBlock |
||||||
|
} |
||||||
|
} |
||||||
|
if header.Time.Cmp(parent.Time) <= 0 { |
||||||
|
return ErrZeroBlockTime |
||||||
|
} |
||||||
|
// Verify the block's difficulty based in it's timestamp and parent's difficulty
|
||||||
|
expected := CalcDifficulty(chain.Config(), header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty) |
||||||
|
if expected.Cmp(header.Difficulty) != 0 { |
||||||
|
return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, expected) |
||||||
|
} |
||||||
|
// Verify that the gas limit remains within allowed bounds
|
||||||
|
diff := new(big.Int).Set(parent.GasLimit) |
||||||
|
diff = diff.Sub(diff, header.GasLimit) |
||||||
|
diff.Abs(diff) |
||||||
|
|
||||||
|
limit := new(big.Int).Set(parent.GasLimit) |
||||||
|
limit = limit.Div(limit, params.GasLimitBoundDivisor) |
||||||
|
|
||||||
|
if diff.Cmp(limit) >= 0 || header.GasLimit.Cmp(params.MinGasLimit) < 0 { |
||||||
|
return fmt.Errorf("invalid gas limit: have %v, want %v += %v", header.GasLimit, parent.GasLimit, limit) |
||||||
|
} |
||||||
|
// Verify that the block number is parent's +1
|
||||||
|
if diff := new(big.Int).Sub(header.Number, parent.Number); diff.Cmp(big.NewInt(1)) != 0 { |
||||||
|
return ErrInvalidNumber |
||||||
|
} |
||||||
|
// Verify the engine specific seal securing the block
|
||||||
|
if seal { |
||||||
|
if err := ethash.VerifySeal(chain, header); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
// If all checks passed, validate any special fields for hard forks
|
||||||
|
if err := misc.VerifyDAOHeaderExtraData(chain.Config(), header); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if err := misc.VerifyForkHashes(chain.Config(), header, uncle); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// CalcDifficulty 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.
|
||||||
|
//
|
||||||
|
// TODO (karalabe): Move the chain maker into this package and make this private!
|
||||||
|
func CalcDifficulty(config *params.ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { |
||||||
|
if config.IsHomestead(new(big.Int).Add(parentNumber, common.Big1)) { |
||||||
|
return calcDifficultyHomestead(time, parentTime, parentNumber, parentDiff) |
||||||
|
} |
||||||
|
return calcDifficultyFrontier(time, parentTime, parentNumber, parentDiff) |
||||||
|
} |
||||||
|
|
||||||
|
// Some weird constants to avoid constant memory allocs for them.
|
||||||
|
var ( |
||||||
|
expDiffPeriod = big.NewInt(100000) |
||||||
|
big10 = big.NewInt(10) |
||||||
|
bigMinus99 = big.NewInt(-99) |
||||||
|
) |
||||||
|
|
||||||
|
// calcDifficultyHomestead 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 Homestead rules.
|
||||||
|
func calcDifficultyHomestead(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { |
||||||
|
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.mediawiki
|
||||||
|
// algorithm:
|
||||||
|
// diff = (parent_diff +
|
||||||
|
// (parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
|
||||||
|
// ) + 2^(periodCount - 2)
|
||||||
|
|
||||||
|
bigTime := new(big.Int).SetUint64(time) |
||||||
|
bigParentTime := new(big.Int).SetUint64(parentTime) |
||||||
|
|
||||||
|
// holds intermediate values to make the algo easier to read & audit
|
||||||
|
x := new(big.Int) |
||||||
|
y := new(big.Int) |
||||||
|
|
||||||
|
// 1 - (block_timestamp -parent_timestamp) // 10
|
||||||
|
x.Sub(bigTime, bigParentTime) |
||||||
|
x.Div(x, big10) |
||||||
|
x.Sub(common.Big1, x) |
||||||
|
|
||||||
|
// max(1 - (block_timestamp - parent_timestamp) // 10, -99)))
|
||||||
|
if x.Cmp(bigMinus99) < 0 { |
||||||
|
x.Set(bigMinus99) |
||||||
|
} |
||||||
|
// (parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
|
||||||
|
y.Div(parentDiff, params.DifficultyBoundDivisor) |
||||||
|
x.Mul(y, x) |
||||||
|
x.Add(parentDiff, x) |
||||||
|
|
||||||
|
// minimum difficulty can ever be (before exponential factor)
|
||||||
|
if x.Cmp(params.MinimumDifficulty) < 0 { |
||||||
|
x.Set(params.MinimumDifficulty) |
||||||
|
} |
||||||
|
// for the exponential factor
|
||||||
|
periodCount := new(big.Int).Add(parentNumber, common.Big1) |
||||||
|
periodCount.Div(periodCount, expDiffPeriod) |
||||||
|
|
||||||
|
// the exponential factor, commonly referred to as "the bomb"
|
||||||
|
// diff = diff + 2^(periodCount - 2)
|
||||||
|
if periodCount.Cmp(common.Big1) > 0 { |
||||||
|
y.Sub(periodCount, common.Big2) |
||||||
|
y.Exp(common.Big2, y, nil) |
||||||
|
x.Add(x, y) |
||||||
|
} |
||||||
|
return x |
||||||
|
} |
||||||
|
|
||||||
|
// calcDifficultyFrontier 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 Frontier rules.
|
||||||
|
func calcDifficultyFrontier(time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int { |
||||||
|
diff := new(big.Int) |
||||||
|
adjust := new(big.Int).Div(parentDiff, params.DifficultyBoundDivisor) |
||||||
|
bigTime := new(big.Int) |
||||||
|
bigParentTime := new(big.Int) |
||||||
|
|
||||||
|
bigTime.SetUint64(time) |
||||||
|
bigParentTime.SetUint64(parentTime) |
||||||
|
|
||||||
|
if bigTime.Sub(bigTime, bigParentTime).Cmp(params.DurationLimit) < 0 { |
||||||
|
diff.Add(parentDiff, adjust) |
||||||
|
} else { |
||||||
|
diff.Sub(parentDiff, adjust) |
||||||
|
} |
||||||
|
if diff.Cmp(params.MinimumDifficulty) < 0 { |
||||||
|
diff.Set(params.MinimumDifficulty) |
||||||
|
} |
||||||
|
|
||||||
|
periodCount := new(big.Int).Add(parentNumber, common.Big1) |
||||||
|
periodCount.Div(periodCount, expDiffPeriod) |
||||||
|
if periodCount.Cmp(common.Big1) > 0 { |
||||||
|
// diff = diff + 2^(periodCount - 2)
|
||||||
|
expDiff := periodCount.Sub(periodCount, common.Big2) |
||||||
|
expDiff.Exp(common.Big2, expDiff, nil) |
||||||
|
diff.Add(diff, expDiff) |
||||||
|
diff = math.BigMax(diff, params.MinimumDifficulty) |
||||||
|
} |
||||||
|
return diff |
||||||
|
} |
||||||
|
|
||||||
|
// VerifySeal implements consensus.Engine, checking whether the given block satisfies
|
||||||
|
// the PoW difficulty requirements.
|
||||||
|
func (ethash *Ethash) VerifySeal(chain consensus.ChainReader, header *types.Header) error { |
||||||
|
// If we're running a fake PoW, accept any seal as valid
|
||||||
|
if ethash.fakeMode { |
||||||
|
time.Sleep(ethash.fakeDelay) |
||||||
|
if ethash.fakeFail == header.Number.Uint64() { |
||||||
|
return ErrInvalidPoW |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
// If we're running a shared PoW, delegate verification to it
|
||||||
|
if ethash.shared != nil { |
||||||
|
return ethash.shared.VerifySeal(chain, header) |
||||||
|
} |
||||||
|
// Sanity check that the block number is below the lookup table size (60M blocks)
|
||||||
|
number := header.Number.Uint64() |
||||||
|
if number/epochLength >= uint64(len(cacheSizes)) { |
||||||
|
// Go < 1.7 cannot calculate new cache/dataset sizes (no fast prime check)
|
||||||
|
return ErrNonceOutOfRange |
||||||
|
} |
||||||
|
// Ensure that we have a valid difficulty for the block
|
||||||
|
if header.Difficulty.Sign() <= 0 { |
||||||
|
return ErrInvalidDifficulty |
||||||
|
} |
||||||
|
// Recompute the digest and PoW value and verify against the header
|
||||||
|
cache := ethash.cache(number) |
||||||
|
|
||||||
|
size := datasetSize(number) |
||||||
|
if ethash.tester { |
||||||
|
size = 32 * 1024 |
||||||
|
} |
||||||
|
digest, result := hashimotoLight(size, cache, header.HashNoNonce().Bytes(), header.Nonce.Uint64()) |
||||||
|
if !bytes.Equal(header.MixDigest[:], digest) { |
||||||
|
return ErrInvalidMixDigest |
||||||
|
} |
||||||
|
target := new(big.Int).Div(maxUint256, header.Difficulty) |
||||||
|
if new(big.Int).SetBytes(result).Cmp(target) > 0 { |
||||||
|
return ErrInvalidPoW |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Prepare implements consensus.Engine, initializing the difficulty field of a
|
||||||
|
// header to conform to the ethash protocol. The changes are done inline.
|
||||||
|
func (ethash *Ethash) Prepare(chain consensus.ChainReader, header *types.Header) error { |
||||||
|
parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) |
||||||
|
if parent == nil { |
||||||
|
return ErrParentUnknown |
||||||
|
} |
||||||
|
header.Difficulty = CalcDifficulty(chain.Config(), header.Time.Uint64(), |
||||||
|
parent.Time.Uint64(), parent.Number, parent.Difficulty) |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Finalize implements consensus.Engine, accumulating the block and uncle rewards,
|
||||||
|
// setting the final state and assembling the block.
|
||||||
|
func (ethash *Ethash) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { |
||||||
|
// Accumulate any block and uncle rewards and commit the final state root
|
||||||
|
AccumulateRewards(state, header, uncles) |
||||||
|
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) |
||||||
|
|
||||||
|
// Header seems complete, assemble into a block and return
|
||||||
|
return types.NewBlock(header, txs, uncles, receipts), nil |
||||||
|
} |
||||||
|
|
||||||
|
// Some weird constants to avoid constant memory allocs for them.
|
||||||
|
var ( |
||||||
|
big8 = big.NewInt(8) |
||||||
|
big32 = big.NewInt(32) |
||||||
|
) |
||||||
|
|
||||||
|
// AccumulateRewards credits the coinbase of the given block with the mining
|
||||||
|
// reward. The total reward consists of the static block reward and rewards for
|
||||||
|
// included uncles. The coinbase of each uncle block is also rewarded.
|
||||||
|
//
|
||||||
|
// TODO (karalabe): Move the chain maker into this package and make this private!
|
||||||
|
func AccumulateRewards(state *state.StateDB, header *types.Header, uncles []*types.Header) { |
||||||
|
reward := new(big.Int).Set(blockReward) |
||||||
|
r := new(big.Int) |
||||||
|
for _, uncle := range uncles { |
||||||
|
r.Add(uncle.Number, big8) |
||||||
|
r.Sub(r, header.Number) |
||||||
|
r.Mul(r, blockReward) |
||||||
|
r.Div(r, big8) |
||||||
|
state.AddBalance(uncle.Coinbase, r) |
||||||
|
|
||||||
|
r.Div(blockReward, big32) |
||||||
|
reward.Add(reward, r) |
||||||
|
} |
||||||
|
state.AddBalance(header.Coinbase, reward) |
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
// Copyright 2017 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 ethash |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"math/big" |
||||||
|
"os" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/math" |
||||||
|
"github.com/ethereum/go-ethereum/params" |
||||||
|
) |
||||||
|
|
||||||
|
type diffTest struct { |
||||||
|
ParentTimestamp uint64 |
||||||
|
ParentDifficulty *big.Int |
||||||
|
CurrentTimestamp uint64 |
||||||
|
CurrentBlocknumber *big.Int |
||||||
|
CurrentDifficulty *big.Int |
||||||
|
} |
||||||
|
|
||||||
|
func (d *diffTest) UnmarshalJSON(b []byte) (err error) { |
||||||
|
var ext struct { |
||||||
|
ParentTimestamp string |
||||||
|
ParentDifficulty string |
||||||
|
CurrentTimestamp string |
||||||
|
CurrentBlocknumber string |
||||||
|
CurrentDifficulty string |
||||||
|
} |
||||||
|
if err := json.Unmarshal(b, &ext); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
d.ParentTimestamp = math.MustParseUint64(ext.ParentTimestamp) |
||||||
|
d.ParentDifficulty = math.MustParseBig256(ext.ParentDifficulty) |
||||||
|
d.CurrentTimestamp = math.MustParseUint64(ext.CurrentTimestamp) |
||||||
|
d.CurrentBlocknumber = math.MustParseBig256(ext.CurrentBlocknumber) |
||||||
|
d.CurrentDifficulty = math.MustParseBig256(ext.CurrentDifficulty) |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func TestCalcDifficulty(t *testing.T) { |
||||||
|
file, err := os.Open("../../tests/files/BasicTests/difficulty.json") |
||||||
|
if err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
defer file.Close() |
||||||
|
|
||||||
|
tests := make(map[string]diffTest) |
||||||
|
err = json.NewDecoder(file).Decode(&tests) |
||||||
|
if err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
|
||||||
|
config := ¶ms.ChainConfig{HomesteadBlock: big.NewInt(1150000)} |
||||||
|
for name, test := range tests { |
||||||
|
number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1)) |
||||||
|
diff := CalcDifficulty(config, test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty) |
||||||
|
if diff.Cmp(test.CurrentDifficulty) != 0 { |
||||||
|
t.Error(name, "failed. Expected", test.CurrentDifficulty, "and calculated", diff) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
// Copyright 2017 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 ethash |
||||||
|
|
||||||
|
import ( |
||||||
|
"math/big" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
) |
||||||
|
|
||||||
|
// Tests that ethash works correctly in test mode.
|
||||||
|
func TestTestMode(t *testing.T) { |
||||||
|
head := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)} |
||||||
|
|
||||||
|
ethash := NewTester() |
||||||
|
block, err := ethash.Seal(nil, types.NewBlockWithHeader(head), nil) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("failed to seal block: %v", err) |
||||||
|
} |
||||||
|
head.Nonce = types.EncodeNonce(block.Nonce()) |
||||||
|
head.MixDigest = block.MixDigest() |
||||||
|
if err := ethash.VerifySeal(nil, head); err != nil { |
||||||
|
t.Fatalf("unexpected verification error: %v", err) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,146 @@ |
|||||||
|
// Copyright 2017 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 ethash |
||||||
|
|
||||||
|
import ( |
||||||
|
crand "crypto/rand" |
||||||
|
"math" |
||||||
|
"math/big" |
||||||
|
"math/rand" |
||||||
|
"runtime" |
||||||
|
"sync" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/consensus" |
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
"github.com/ethereum/go-ethereum/log" |
||||||
|
) |
||||||
|
|
||||||
|
// Seal implements consensus.Engine, attempting to find a nonce that satisfies
|
||||||
|
// the block's difficulty requirements.
|
||||||
|
func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error) { |
||||||
|
// If we're running a fake PoW, simply return a 0 nonce immediately
|
||||||
|
if ethash.fakeMode { |
||||||
|
header := block.Header() |
||||||
|
header.Nonce, header.MixDigest = types.BlockNonce{}, common.Hash{} |
||||||
|
return block.WithSeal(header), nil |
||||||
|
} |
||||||
|
// If we're running a shared PoW, delegate sealing to it
|
||||||
|
if ethash.shared != nil { |
||||||
|
return ethash.shared.Seal(chain, block, stop) |
||||||
|
} |
||||||
|
// Create a runner and the multiple search threads it directs
|
||||||
|
abort := make(chan struct{}) |
||||||
|
found := make(chan *types.Block) |
||||||
|
|
||||||
|
ethash.lock.Lock() |
||||||
|
threads := ethash.threads |
||||||
|
if ethash.rand == nil { |
||||||
|
seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) |
||||||
|
if err != nil { |
||||||
|
ethash.lock.Unlock() |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
ethash.rand = rand.New(rand.NewSource(seed.Int64())) |
||||||
|
} |
||||||
|
ethash.lock.Unlock() |
||||||
|
if threads == 0 { |
||||||
|
threads = runtime.NumCPU() |
||||||
|
} |
||||||
|
var pend sync.WaitGroup |
||||||
|
for i := 0; i < threads; i++ { |
||||||
|
pend.Add(1) |
||||||
|
go func(id int, nonce uint64) { |
||||||
|
defer pend.Done() |
||||||
|
ethash.mine(block, id, nonce, abort, found) |
||||||
|
}(i, uint64(ethash.rand.Int63())) |
||||||
|
} |
||||||
|
// Wait until sealing is terminated or a nonce is found
|
||||||
|
var result *types.Block |
||||||
|
select { |
||||||
|
case <-stop: |
||||||
|
// Outside abort, stop all miner threads
|
||||||
|
close(abort) |
||||||
|
case result = <-found: |
||||||
|
// One of the threads found a block, abort all others
|
||||||
|
close(abort) |
||||||
|
case <-ethash.update: |
||||||
|
// Thread count was changed on user request, restart
|
||||||
|
close(abort) |
||||||
|
pend.Wait() |
||||||
|
return ethash.Seal(chain, block, stop) |
||||||
|
} |
||||||
|
// Wait for all miners to terminate and return the block
|
||||||
|
pend.Wait() |
||||||
|
return result, nil |
||||||
|
} |
||||||
|
|
||||||
|
// mine is the actual proof-of-work miner that searches for a nonce starting from
|
||||||
|
// seed that results in correct final block difficulty.
|
||||||
|
func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan struct{}, found chan *types.Block) { |
||||||
|
// Extract some data from the header
|
||||||
|
var ( |
||||||
|
header = block.Header() |
||||||
|
hash = header.HashNoNonce().Bytes() |
||||||
|
target = new(big.Int).Div(maxUint256, header.Difficulty) |
||||||
|
|
||||||
|
number = header.Number.Uint64() |
||||||
|
dataset = ethash.dataset(number) |
||||||
|
) |
||||||
|
// Start generating random nonces until we abort or find a good one
|
||||||
|
var ( |
||||||
|
attempts = int64(0) |
||||||
|
nonce = seed |
||||||
|
) |
||||||
|
logger := log.New("miner", id) |
||||||
|
logger.Trace("Started ethash search for new nonces", "seed", seed) |
||||||
|
for { |
||||||
|
select { |
||||||
|
case <-abort: |
||||||
|
// Mining terminated, update stats and abort
|
||||||
|
logger.Trace("Ethash nonce search aborted", "attempts", nonce-seed) |
||||||
|
ethash.hashrate.Mark(attempts) |
||||||
|
return |
||||||
|
|
||||||
|
default: |
||||||
|
// We don't have to update hash rate on every nonce, so update after after 2^X nonces
|
||||||
|
attempts++ |
||||||
|
if (attempts % (1 << 15)) == 0 { |
||||||
|
ethash.hashrate.Mark(attempts) |
||||||
|
attempts = 0 |
||||||
|
} |
||||||
|
// Compute the PoW value of this nonce
|
||||||
|
digest, result := hashimotoFull(dataset, hash, nonce) |
||||||
|
if new(big.Int).SetBytes(result).Cmp(target) <= 0 { |
||||||
|
// Correct nonce found, create a new header with it
|
||||||
|
header = types.CopyHeader(header) |
||||||
|
header.Nonce = types.EncodeNonce(nonce) |
||||||
|
header.MixDigest = common.BytesToHash(digest) |
||||||
|
|
||||||
|
// Seal and return a block (if still needed)
|
||||||
|
select { |
||||||
|
case found <- block.WithSeal(header): |
||||||
|
logger.Trace("Ethash nonce found and reported", "attempts", nonce-seed, "nonce", nonce) |
||||||
|
case <-abort: |
||||||
|
logger.Trace("Ethash nonce found but discarded", "attempts", nonce-seed, "nonce", nonce) |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
nonce++ |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,85 @@ |
|||||||
|
// 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 misc |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"errors" |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/state" |
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
"github.com/ethereum/go-ethereum/params" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
// ErrBadProDAOExtra is returned if a header doens't support the DAO fork on a
|
||||||
|
// pro-fork client.
|
||||||
|
ErrBadProDAOExtra = errors.New("bad DAO pro-fork extra-data") |
||||||
|
|
||||||
|
// ErrBadNoDAOExtra is returned if a header does support the DAO fork on a no-
|
||||||
|
// fork client.
|
||||||
|
ErrBadNoDAOExtra = errors.New("bad DAO no-fork extra-data") |
||||||
|
) |
||||||
|
|
||||||
|
// VerifyDAOHeaderExtraData validates the extra-data field of a block header to
|
||||||
|
// ensure it conforms to DAO hard-fork rules.
|
||||||
|
//
|
||||||
|
// DAO hard-fork extension to the header validity:
|
||||||
|
// a) if the node is no-fork, do not accept blocks in the [fork, fork+10) range
|
||||||
|
// with the fork specific extra-data set
|
||||||
|
// b) if the node is pro-fork, require blocks in the specific range to have the
|
||||||
|
// unique extra-data set.
|
||||||
|
func VerifyDAOHeaderExtraData(config *params.ChainConfig, header *types.Header) error { |
||||||
|
// Short circuit validation if the node doesn't care about the DAO fork
|
||||||
|
if config.DAOForkBlock == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
// Make sure the block is within the fork's modified extra-data range
|
||||||
|
limit := new(big.Int).Add(config.DAOForkBlock, params.DAOForkExtraRange) |
||||||
|
if header.Number.Cmp(config.DAOForkBlock) < 0 || header.Number.Cmp(limit) >= 0 { |
||||||
|
return nil |
||||||
|
} |
||||||
|
// Depending whether we support or oppose the fork, validate the extra-data contents
|
||||||
|
if config.DAOForkSupport { |
||||||
|
if !bytes.Equal(header.Extra, params.DAOForkBlockExtra) { |
||||||
|
return ErrBadProDAOExtra |
||||||
|
} |
||||||
|
} else { |
||||||
|
if bytes.Equal(header.Extra, params.DAOForkBlockExtra) { |
||||||
|
return ErrBadNoDAOExtra |
||||||
|
} |
||||||
|
} |
||||||
|
// All ok, header has the same extra-data we expect
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// ApplyDAOHardFork modifies the state database according to the DAO hard-fork
|
||||||
|
// rules, transferring all balances of a set of DAO accounts to a single refund
|
||||||
|
// contract.
|
||||||
|
func ApplyDAOHardFork(statedb *state.StateDB) { |
||||||
|
// Retrieve the contract to refund balances into
|
||||||
|
if !statedb.Exist(params.DAORefundContract) { |
||||||
|
statedb.CreateAccount(params.DAORefundContract) |
||||||
|
} |
||||||
|
|
||||||
|
// Move every DAO account and extra-balance account funds into the refund contract
|
||||||
|
for _, addr := range params.DAODrainList() { |
||||||
|
statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr)) |
||||||
|
statedb.SetBalance(addr, new(big.Int)) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
// Copyright 2017 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 misc |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
"github.com/ethereum/go-ethereum/params" |
||||||
|
) |
||||||
|
|
||||||
|
// VerifyForkHashes verifies that blocks conforming to network hard-forks do have
|
||||||
|
// the correct hashes, to avoid clients going off on different chains. This is an
|
||||||
|
// optional feature.
|
||||||
|
func VerifyForkHashes(config *params.ChainConfig, header *types.Header, uncle bool) error { |
||||||
|
// We don't care about uncles
|
||||||
|
if uncle { |
||||||
|
return nil |
||||||
|
} |
||||||
|
// If the homestead reprice hash is set, validate it
|
||||||
|
if config.EIP150Block != nil && config.EIP150Block.Cmp(header.Number) == 0 { |
||||||
|
if config.EIP150Hash != (common.Hash{}) && config.EIP150Hash != header.Hash() { |
||||||
|
return fmt.Errorf("homestead gas reprice fork: have 0x%x, want 0x%x", header.Hash(), config.EIP150Hash) |
||||||
|
} |
||||||
|
} |
||||||
|
// All ok, return
|
||||||
|
return nil |
||||||
|
} |
@ -1,87 +0,0 @@ |
|||||||
// Copyright 2015 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 core |
|
||||||
|
|
||||||
import ( |
|
||||||
"runtime" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/types" |
|
||||||
"github.com/ethereum/go-ethereum/pow" |
|
||||||
) |
|
||||||
|
|
||||||
// nonceCheckResult contains the result of a nonce verification.
|
|
||||||
type nonceCheckResult struct { |
|
||||||
index int // Index of the item verified from an input array
|
|
||||||
valid bool // Result of the nonce verification
|
|
||||||
} |
|
||||||
|
|
||||||
// verifyNoncesFromHeaders starts a concurrent header nonce verification,
|
|
||||||
// returning a quit channel to abort the operations and a results channel
|
|
||||||
// to retrieve the async verifications.
|
|
||||||
func verifyNoncesFromHeaders(checker pow.PoW, headers []*types.Header) (chan<- struct{}, <-chan nonceCheckResult) { |
|
||||||
items := make([]pow.Block, len(headers)) |
|
||||||
for i, header := range headers { |
|
||||||
items[i] = types.NewBlockWithHeader(header) |
|
||||||
} |
|
||||||
return verifyNonces(checker, items) |
|
||||||
} |
|
||||||
|
|
||||||
// verifyNoncesFromBlocks starts a concurrent block nonce verification,
|
|
||||||
// returning a quit channel to abort the operations and a results channel
|
|
||||||
// to retrieve the async verifications.
|
|
||||||
func verifyNoncesFromBlocks(checker pow.PoW, blocks []*types.Block) (chan<- struct{}, <-chan nonceCheckResult) { |
|
||||||
items := make([]pow.Block, len(blocks)) |
|
||||||
for i, block := range blocks { |
|
||||||
items[i] = block |
|
||||||
} |
|
||||||
return verifyNonces(checker, items) |
|
||||||
} |
|
||||||
|
|
||||||
// verifyNonces starts a concurrent nonce verification, returning a quit channel
|
|
||||||
// to abort the operations and a results channel to retrieve the async checks.
|
|
||||||
func verifyNonces(checker pow.PoW, items []pow.Block) (chan<- struct{}, <-chan nonceCheckResult) { |
|
||||||
// Spawn as many workers as allowed threads
|
|
||||||
workers := runtime.GOMAXPROCS(0) |
|
||||||
if len(items) < workers { |
|
||||||
workers = len(items) |
|
||||||
} |
|
||||||
// Create a task channel and spawn the verifiers
|
|
||||||
tasks := make(chan int, workers) |
|
||||||
results := make(chan nonceCheckResult, len(items)) // Buffered to make sure all workers stop
|
|
||||||
for i := 0; i < workers; i++ { |
|
||||||
go func() { |
|
||||||
for index := range tasks { |
|
||||||
results <- nonceCheckResult{index: index, valid: checker.Verify(items[index]) == nil} |
|
||||||
} |
|
||||||
}() |
|
||||||
} |
|
||||||
// Feed item indices to the workers until done or aborted
|
|
||||||
abort := make(chan struct{}) |
|
||||||
go func() { |
|
||||||
defer close(tasks) |
|
||||||
|
|
||||||
for i := range items { |
|
||||||
select { |
|
||||||
case tasks <- i: |
|
||||||
continue |
|
||||||
case <-abort: |
|
||||||
return |
|
||||||
} |
|
||||||
} |
|
||||||
}() |
|
||||||
return abort, results |
|
||||||
} |
|
@ -1,238 +0,0 @@ |
|||||||
// Copyright 2015 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 core |
|
||||||
|
|
||||||
import ( |
|
||||||
"errors" |
|
||||||
"math/big" |
|
||||||
"runtime" |
|
||||||
"testing" |
|
||||||
"time" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/core/types" |
|
||||||
"github.com/ethereum/go-ethereum/ethdb" |
|
||||||
"github.com/ethereum/go-ethereum/params" |
|
||||||
"github.com/ethereum/go-ethereum/pow" |
|
||||||
) |
|
||||||
|
|
||||||
// failPow is a non-validating proof of work implementation, that returns true
|
|
||||||
// from Verify for all but one block.
|
|
||||||
type failPow struct { |
|
||||||
failing uint64 |
|
||||||
} |
|
||||||
|
|
||||||
func (pow failPow) Search(pow.Block, <-chan struct{}) (uint64, []byte) { |
|
||||||
return 0, nil |
|
||||||
} |
|
||||||
func (pow failPow) Verify(block pow.Block) error { |
|
||||||
if block.NumberU64() == pow.failing { |
|
||||||
return errors.New("failed") |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
func (pow failPow) Hashrate() float64 { return 0 } |
|
||||||
|
|
||||||
// delayedPow is a non-validating proof of work implementation, that returns true
|
|
||||||
// from Verify for all blocks, but delays them the configured amount of time.
|
|
||||||
type delayedPow struct { |
|
||||||
delay time.Duration |
|
||||||
} |
|
||||||
|
|
||||||
func (pow delayedPow) Search(pow.Block, <-chan struct{}) (uint64, []byte) { |
|
||||||
return 0, nil |
|
||||||
} |
|
||||||
func (pow delayedPow) Verify(block pow.Block) error { time.Sleep(pow.delay); return nil } |
|
||||||
func (pow delayedPow) Hashrate() float64 { return 0 } |
|
||||||
|
|
||||||
// Tests that simple POW verification works, for both good and bad blocks.
|
|
||||||
func TestPowVerification(t *testing.T) { |
|
||||||
// Create a simple chain to verify
|
|
||||||
var ( |
|
||||||
testdb, _ = ethdb.NewMemDatabase() |
|
||||||
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int)) |
|
||||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil) |
|
||||||
) |
|
||||||
headers := make([]*types.Header, len(blocks)) |
|
||||||
for i, block := range blocks { |
|
||||||
headers[i] = block.Header() |
|
||||||
} |
|
||||||
// Run the POW checker for blocks one-by-one, checking for both valid and invalid nonces
|
|
||||||
for i := 0; i < len(blocks); i++ { |
|
||||||
for j, full := range []bool{true, false} { |
|
||||||
for k, valid := range []bool{true, false} { |
|
||||||
var results <-chan nonceCheckResult |
|
||||||
|
|
||||||
switch { |
|
||||||
case full && valid: |
|
||||||
_, results = verifyNoncesFromBlocks(pow.FakePow{}, []*types.Block{blocks[i]}) |
|
||||||
case full && !valid: |
|
||||||
_, results = verifyNoncesFromBlocks(failPow{blocks[i].NumberU64()}, []*types.Block{blocks[i]}) |
|
||||||
case !full && valid: |
|
||||||
_, results = verifyNoncesFromHeaders(pow.FakePow{}, []*types.Header{headers[i]}) |
|
||||||
case !full && !valid: |
|
||||||
_, results = verifyNoncesFromHeaders(failPow{headers[i].Number.Uint64()}, []*types.Header{headers[i]}) |
|
||||||
} |
|
||||||
// Wait for the verification result
|
|
||||||
select { |
|
||||||
case result := <-results: |
|
||||||
if result.index != 0 { |
|
||||||
t.Errorf("test %d.%d.%d: invalid index: have %d, want 0", i, j, k, result.index) |
|
||||||
} |
|
||||||
if result.valid != valid { |
|
||||||
t.Errorf("test %d.%d.%d: validity mismatch: have %v, want %v", i, j, k, result.valid, valid) |
|
||||||
} |
|
||||||
case <-time.After(time.Second): |
|
||||||
t.Fatalf("test %d.%d.%d: verification timeout", i, j, k) |
|
||||||
} |
|
||||||
// Make sure no more data is returned
|
|
||||||
select { |
|
||||||
case result := <-results: |
|
||||||
t.Fatalf("test %d.%d.%d: unexpected result returned: %v", i, j, k, result) |
|
||||||
case <-time.After(25 * time.Millisecond): |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Tests that concurrent POW verification works, for both good and bad blocks.
|
|
||||||
func TestPowConcurrentVerification2(t *testing.T) { testPowConcurrentVerification(t, 2) } |
|
||||||
func TestPowConcurrentVerification8(t *testing.T) { testPowConcurrentVerification(t, 8) } |
|
||||||
func TestPowConcurrentVerification32(t *testing.T) { testPowConcurrentVerification(t, 32) } |
|
||||||
|
|
||||||
func testPowConcurrentVerification(t *testing.T, threads int) { |
|
||||||
// Create a simple chain to verify
|
|
||||||
var ( |
|
||||||
testdb, _ = ethdb.NewMemDatabase() |
|
||||||
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int)) |
|
||||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil) |
|
||||||
) |
|
||||||
headers := make([]*types.Header, len(blocks)) |
|
||||||
for i, block := range blocks { |
|
||||||
headers[i] = block.Header() |
|
||||||
} |
|
||||||
// Set the number of threads to verify on
|
|
||||||
old := runtime.GOMAXPROCS(threads) |
|
||||||
defer runtime.GOMAXPROCS(old) |
|
||||||
|
|
||||||
// Run the POW checker for the entire block chain at once both for a valid and
|
|
||||||
// also an invalid chain (enough if one is invalid, last but one (arbitrary)).
|
|
||||||
for i, full := range []bool{true, false} { |
|
||||||
for j, valid := range []bool{true, false} { |
|
||||||
var results <-chan nonceCheckResult |
|
||||||
|
|
||||||
switch { |
|
||||||
case full && valid: |
|
||||||
_, results = verifyNoncesFromBlocks(pow.FakePow{}, blocks) |
|
||||||
case full && !valid: |
|
||||||
_, results = verifyNoncesFromBlocks(failPow{uint64(len(blocks) - 1)}, blocks) |
|
||||||
case !full && valid: |
|
||||||
_, results = verifyNoncesFromHeaders(pow.FakePow{}, headers) |
|
||||||
case !full && !valid: |
|
||||||
_, results = verifyNoncesFromHeaders(failPow{uint64(len(headers) - 1)}, headers) |
|
||||||
} |
|
||||||
// Wait for all the verification results
|
|
||||||
checks := make(map[int]bool) |
|
||||||
for k := 0; k < len(blocks); k++ { |
|
||||||
select { |
|
||||||
case result := <-results: |
|
||||||
if _, ok := checks[result.index]; ok { |
|
||||||
t.Fatalf("test %d.%d.%d: duplicate results for %d", i, j, k, result.index) |
|
||||||
} |
|
||||||
if result.index < 0 || result.index >= len(blocks) { |
|
||||||
t.Fatalf("test %d.%d.%d: result %d out of bounds [%d, %d]", i, j, k, result.index, 0, len(blocks)-1) |
|
||||||
} |
|
||||||
checks[result.index] = result.valid |
|
||||||
|
|
||||||
case <-time.After(time.Second): |
|
||||||
t.Fatalf("test %d.%d.%d: verification timeout", i, j, k) |
|
||||||
} |
|
||||||
} |
|
||||||
// Check nonce check validity
|
|
||||||
for k := 0; k < len(blocks); k++ { |
|
||||||
want := valid || (k != len(blocks)-2) // We chose the last but one nonce in the chain to fail
|
|
||||||
if checks[k] != want { |
|
||||||
t.Errorf("test %d.%d.%d: validity mismatch: have %v, want %v", i, j, k, checks[k], want) |
|
||||||
} |
|
||||||
} |
|
||||||
// Make sure no more data is returned
|
|
||||||
select { |
|
||||||
case result := <-results: |
|
||||||
t.Fatalf("test %d.%d: unexpected result returned: %v", i, j, result) |
|
||||||
case <-time.After(25 * time.Millisecond): |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Tests that aborting a POW validation indeed prevents further checks from being
|
|
||||||
// run, as well as checks that no left-over goroutines are leaked.
|
|
||||||
func TestPowConcurrentAbortion2(t *testing.T) { testPowConcurrentAbortion(t, 2) } |
|
||||||
func TestPowConcurrentAbortion8(t *testing.T) { testPowConcurrentAbortion(t, 8) } |
|
||||||
func TestPowConcurrentAbortion32(t *testing.T) { testPowConcurrentAbortion(t, 32) } |
|
||||||
|
|
||||||
func testPowConcurrentAbortion(t *testing.T, threads int) { |
|
||||||
// Create a simple chain to verify
|
|
||||||
var ( |
|
||||||
testdb, _ = ethdb.NewMemDatabase() |
|
||||||
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int)) |
|
||||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 1024, nil) |
|
||||||
) |
|
||||||
headers := make([]*types.Header, len(blocks)) |
|
||||||
for i, block := range blocks { |
|
||||||
headers[i] = block.Header() |
|
||||||
} |
|
||||||
// Set the number of threads to verify on
|
|
||||||
old := runtime.GOMAXPROCS(threads) |
|
||||||
defer runtime.GOMAXPROCS(old) |
|
||||||
|
|
||||||
// Run the POW checker for the entire block chain at once
|
|
||||||
for i, full := range []bool{true, false} { |
|
||||||
var abort chan<- struct{} |
|
||||||
var results <-chan nonceCheckResult |
|
||||||
|
|
||||||
// Start the verifications and immediately abort
|
|
||||||
if full { |
|
||||||
abort, results = verifyNoncesFromBlocks(delayedPow{time.Millisecond}, blocks) |
|
||||||
} else { |
|
||||||
abort, results = verifyNoncesFromHeaders(delayedPow{time.Millisecond}, headers) |
|
||||||
} |
|
||||||
close(abort) |
|
||||||
|
|
||||||
// Deplete the results channel
|
|
||||||
verified := make(map[int]struct{}) |
|
||||||
for depleted := false; !depleted; { |
|
||||||
select { |
|
||||||
case result := <-results: |
|
||||||
verified[result.index] = struct{}{} |
|
||||||
case <-time.After(50 * time.Millisecond): |
|
||||||
depleted = true |
|
||||||
} |
|
||||||
} |
|
||||||
// Check that abortion was honored by not processing too many POWs
|
|
||||||
if len(verified) > 2*threads { |
|
||||||
t.Errorf("test %d: verification count too large: have %d, want below %d", i, len(verified), 2*threads) |
|
||||||
} |
|
||||||
// Check that there are no gaps in the results
|
|
||||||
for j := 0; j < len(verified); j++ { |
|
||||||
if _, ok := verified[j]; !ok { |
|
||||||
t.Errorf("test %d.%d: gap found in verification results", i, j) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,58 +0,0 @@ |
|||||||
// Copyright 2014 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 pow |
|
||||||
|
|
||||||
import ( |
|
||||||
"math/big" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/core/types" |
|
||||||
) |
|
||||||
|
|
||||||
type Block interface { |
|
||||||
Difficulty() *big.Int |
|
||||||
HashNoNonce() common.Hash |
|
||||||
Nonce() uint64 |
|
||||||
MixDigest() common.Hash |
|
||||||
NumberU64() uint64 |
|
||||||
} |
|
||||||
|
|
||||||
type ChainManager interface { |
|
||||||
GetBlockByNumber(uint64) *types.Block |
|
||||||
CurrentBlock() *types.Block |
|
||||||
} |
|
||||||
|
|
||||||
type PoW interface { |
|
||||||
Verify(block Block) error |
|
||||||
Search(block Block, stop <-chan struct{}) (uint64, []byte) |
|
||||||
Hashrate() float64 |
|
||||||
} |
|
||||||
|
|
||||||
// FakePow is a non-validating proof of work implementation.
|
|
||||||
// It returns true from Verify for any block.
|
|
||||||
type FakePow struct{} |
|
||||||
|
|
||||||
// Verify implements PoW, returning a success for an input.
|
|
||||||
func (pow FakePow) Verify(block Block) error { return nil } |
|
||||||
|
|
||||||
// Search implements PoW, returning the nonce 0 for any call.
|
|
||||||
func (pow FakePow) Search(block Block, stop <-chan struct{}) (uint64, []byte) { |
|
||||||
return 0, nil |
|
||||||
} |
|
||||||
|
|
||||||
// Hashrate implements PoW, returning 0.
|
|
||||||
func (pow FakePow) Hashrate() float64 { return 0 } |
|
Loading…
Reference in new issue