ethash, core, eth, miner, mobile: fix review requests

This commit is contained in:
Péter Szilágyi 2017-03-30 14:04:19 +03:00
parent 6c04b73cbd
commit fc6cf3f8cf
No known key found for this signature in database
GPG key ID: E9AE538CEDF8293D
9 changed files with 64 additions and 114 deletions

View file

@ -22,6 +22,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"runtime" "runtime"
"sync/atomic"
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -40,6 +41,7 @@ var (
) )
var ( var (
ErrInvalidChain = errors.New("invalid header chain")
ErrParentUnknown = errors.New("parent not known locally") ErrParentUnknown = errors.New("parent not known locally")
ErrFutureBlock = errors.New("block in the future") ErrFutureBlock = errors.New("block in the future")
ErrLargeBlockTimestamp = errors.New("timestamp too big") ErrLargeBlockTimestamp = errors.New("timestamp too big")
@ -100,9 +102,15 @@ func (ethash *Ethash) VerifyHeaders(chain consensus.ChainReader, headers []*type
inputs := make(chan int, workers) inputs := make(chan int, workers)
outputs := make(chan result, len(headers)) outputs := make(chan result, len(headers))
var badblock uint64
for i := 0; i < workers; i++ { for i := 0; i < workers; i++ {
go func() { go func() {
for index := range inputs { 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 // We need to look up the first parent
var parent *types.Header var parent *types.Header
if index == 0 { if index == 0 {
@ -111,13 +119,29 @@ func (ethash *Ethash) VerifyHeaders(chain consensus.ChainReader, headers []*type
parent = headers[index-1] parent = headers[index-1]
} }
// Ensure the validation is useful and execute it // Ensure the validation is useful and execute it
var failure error
switch { switch {
case chain.GetHeader(headers[index].Hash(), headers[index].Number.Uint64()-1) != nil: case chain.GetHeader(headers[index].Hash(), headers[index].Number.Uint64()-1) != nil:
outputs <- result{index: index, err: nil} outputs <- result{index: index, err: nil}
case parent == nil: case parent == nil:
outputs <- result{index: index, err: ErrParentUnknown} failure = ErrParentUnknown
outputs <- result{index: index, err: failure}
default: default:
outputs <- result{index: index, err: ethash.verifyHeader(chain, headers[index], parent, false, seals[index])} 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)
}
} }
} }
}() }()

View file

@ -333,6 +333,7 @@ type Ethash struct {
fdataset *dataset // Pre-generated dataset for the estimated future epoch fdataset *dataset // Pre-generated dataset for the estimated future epoch
// Mining related fields // Mining related fields
rand *rand.Rand // Properly seeded random source for nonces
threads int // Number of threads to mine on if mining threads int // Number of threads to mine on if mining
update chan struct{} // Notification channel to update mining parameters update chan struct{} // Notification channel to update mining parameters
hashrate metrics.Meter // Meter tracking the average hashrate hashrate metrics.Meter // Meter tracking the average hashrate
@ -409,7 +410,7 @@ func NewFakeDelayer(delay time.Duration) *Ethash {
} }
// NewFullFaker creates a ethash consensus engine with a full fake scheme that // NewFullFaker creates a ethash consensus engine with a full fake scheme that
// accepts all blocks as valid, without checking any consenss rules whatsoever. // accepts all blocks as valid, without checking any consensus rules whatsoever.
func NewFullFaker() *Ethash { func NewFullFaker() *Ethash {
return &Ethash{fakeMode: true, fakeFull: true} return &Ethash{fakeMode: true, fakeFull: true}
} }
@ -544,7 +545,7 @@ func (ethash *Ethash) dataset(block uint64) []uint32 {
} }
// Threads returns the number of mining threads currently enabled. This doesn't // Threads returns the number of mining threads currently enabled. This doesn't
// necesarilly mean that mining is running! // necessarily mean that mining is running!
func (ethash *Ethash) Threads() int { func (ethash *Ethash) Threads() int {
ethash.lock.Lock() ethash.lock.Lock()
defer ethash.lock.Unlock() defer ethash.lock.Unlock()

View file

@ -17,6 +17,8 @@
package ethash package ethash
import ( import (
crand "crypto/rand"
"math"
"math/big" "math/big"
"math/rand" "math/rand"
"runtime" "runtime"
@ -47,6 +49,14 @@ func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, stop
ethash.lock.Lock() ethash.lock.Lock()
threads := ethash.threads 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() ethash.lock.Unlock()
if threads == 0 { if threads == 0 {
threads = runtime.NumCPU() threads = runtime.NumCPU()
@ -54,10 +64,10 @@ func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, stop
var pend sync.WaitGroup var pend sync.WaitGroup
for i := 0; i < threads; i++ { for i := 0; i < threads; i++ {
pend.Add(1) pend.Add(1)
go func(id int) { go func(id int, nonce uint64) {
defer pend.Done() defer pend.Done()
ethash.mine(block, id, uint64(rand.Int63()), abort, found) ethash.mine(block, id, nonce, abort, found)
}(i) }(i, uint64(ethash.rand.Int63()))
} }
// Wait until sealing is terminated or a nonce is found // Wait until sealing is terminated or a nonce is found
var result *types.Block var result *types.Block
@ -80,7 +90,7 @@ func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, stop
} }
// mine is the actual proof-of-work miner that searches for a nonce starting from // mine is the actual proof-of-work miner that searches for a nonce starting from
// seed that results in corrent final block difficulty. // 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) { func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan struct{}, found chan *types.Block) {
// Extract some data from the header // Extract some data from the header
var ( var (

View file

@ -1,78 +0,0 @@
// 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/>.
// Contains some utility methods to allow creating hooked consensus structures
// mostly for test code where only a few methods are needed.
package consensus
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
)
type CurrentHeaderFn func() *types.Header
type HeaderRetrievalFn func(hash common.Hash, number uint64) *types.Header
type HeaderByNumberRetrievalFn func(number uint64) *types.Header
type BlockRetrievalFn func(hash common.Hash, number uint64) *types.Block
// hookedChainReader is a tiny implementation of ChainReader based on callbacks
// and a preset chain configuration. It's useful to avoid defining various custom
// types where a cain reader isn't readily available.
type hookedChainReader struct {
config *params.ChainConfig
currentHeader CurrentHeaderFn
getHeader HeaderRetrievalFn
getHeaderByNumber HeaderByNumberRetrievalFn
getBlock BlockRetrievalFn
}
// MakeChainReader creates a callback based chain reader.
func MakeChainReader(config *params.ChainConfig, currentHeader CurrentHeaderFn, getHeader HeaderRetrievalFn, getHeaderByNumber HeaderByNumberRetrievalFn, getBlock BlockRetrievalFn) ChainReader {
return &hookedChainReader{
config: config,
currentHeader: currentHeader,
getHeader: getHeader,
getHeaderByNumber: getHeaderByNumber,
getBlock: getBlock,
}
}
// Config implements ChainReader, retrieving the blockchain's chain configuration.
func (hcr *hookedChainReader) Config() *params.ChainConfig { return hcr.config }
// CurrentHeader implements ChainReader, retrieving the current header.
func (hcr *hookedChainReader) CurrentHeader() *types.Header {
return hcr.currentHeader()
}
// GetHeader implements ChainReader, retrieving a block header from the database
// by hash and number.
func (hcr *hookedChainReader) GetHeader(hash common.Hash, number uint64) *types.Header {
return hcr.getHeader(hash, number)
}
// GetHeaderByNumber implements ChainReader, retrieving a block header from the
// database by number.
func (hcr *hookedChainReader) GetHeaderByNumber(number uint64) *types.Header {
return hcr.getHeaderByNumber(number)
}
// GetBlock implements ChainReader, retrieving a block from the database by hash and number.
func (hcr *hookedChainReader) GetBlock(hash common.Hash, number uint64) *types.Block {
return hcr.getBlock(hash, number)
}

View file

@ -51,7 +51,7 @@ func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engin
// header's transaction and uncle roots. The headers are assumed to be already // header's transaction and uncle roots. The headers are assumed to be already
// validated at this point. // validated at this point.
func (v *BlockValidator) ValidateBody(block *types.Block) error { func (v *BlockValidator) ValidateBody(block *types.Block) error {
// Check whether the blokc's known, and if not, that it's linkable // Check whether the block's known, and if not, that it's linkable
if v.bc.HasBlock(block.Hash()) { if v.bc.HasBlock(block.Hash()) {
if _, err := state.New(block.Root(), v.bc.chainDb); err == nil { if _, err := state.New(block.Root(), v.bc.chainDb); err == nil {
return &KnownBlockError{block.Number(), block.Hash()} return &KnownBlockError{block.Number(), block.Hash()}
@ -70,7 +70,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
return err return err
} }
if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash { if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash {
return fmt.Errorf("uncle root hsh mismatch: have %x, want %x", hash, header.UncleHash) return fmt.Errorf("uncle root hash mismatch: have %x, want %x", hash, header.UncleHash)
} }
if hash := types.DeriveSha(block.Transactions()); hash != header.TxHash { if hash := types.DeriveSha(block.Transactions()); hash != header.TxHash {
return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash) return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash)

View file

@ -101,7 +101,7 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) {
defer runtime.GOMAXPROCS(old) defer runtime.GOMAXPROCS(old)
// Run the header checker for the entire block chain at once both for a valid and // Run the header 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)). // also an invalid chain (enough if one arbitrary block is invalid).
for i, valid := range []bool{true, false} { for i, valid := range []bool{true, false} {
var results <-chan error var results <-chan error
@ -125,10 +125,16 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) {
} }
// Check nonce check validity // Check nonce check validity
for j := 0; j < len(blocks); j++ { for j := 0; j < len(blocks); j++ {
want := valid || (j != len(blocks)-2) // We chose the last but one nonce in the chain to fail want := valid || (j < len(blocks)-2) // We chose the last-but-one nonce in the chain to fail
if (checks[j] == nil) != want { if (checks[j] == nil) != want {
t.Errorf("test %d.%d: validity mismatch: have %v, want %v", i, j, checks[j], want) t.Errorf("test %d.%d: validity mismatch: have %v, want %v", i, j, checks[j], want)
} }
if !want {
// A few blocks after the first error may pass verification due to concurrent
// workers. We don't care about those in this test, just that the correct block
// errors out.
break
}
} }
// Make sure no more data is returned // Make sure no more data is returned
select { select {

View file

@ -133,7 +133,7 @@ func NewPrivateMinerAPI(e *Ethereum) *PrivateMinerAPI {
} }
// Start the miner with the given number of threads. If threads is nil the number // Start the miner with the given number of threads. If threads is nil the number
// of workers started is equal to the number of logical CPU'api that are usable by // of workers started is equal to the number of logical CPUs that are usable by
// this process. If mining is already running, this method adjust the number of // this process. If mining is already running, this method adjust the number of
// threads allowed to use. // threads allowed to use.
func (api *PrivateMinerAPI) Start(threads *int) error { func (api *PrivateMinerAPI) Start(threads *int) error {

View file

@ -131,8 +131,8 @@ func (a *RemoteAgent) GetWork() ([3]string, error) {
return res, errors.New("No work available yet, don't panic.") return res, errors.New("No work available yet, don't panic.")
} }
// SubmitWork tries to inject a pow solution tinto the remote agent, returning // SubmitWork tries to inject a pow solution into the remote agent, returning
// whether the solution was acceted or not (not can be both a bad pow as well as // whether the solution was accepted or not (not can be both a bad pow as well as
// any other error, like no work pending). // any other error, like no work pending).
func (a *RemoteAgent) SubmitWork(nonce types.BlockNonce, mixDigest, hash common.Hash) bool { func (a *RemoteAgent) SubmitWork(nonce types.BlockNonce, mixDigest, hash common.Hash) bool {
a.mu.Lock() a.mu.Lock()

View file

@ -37,7 +37,6 @@ func MainnetChainConfig() *ChainConfig {
EIP150Hash: Hash{params.MainNetHomesteadGasRepriceHash}, EIP150Hash: Hash{params.MainNetHomesteadGasRepriceHash},
EIP155Block: params.MainNetSpuriousDragon.Int64(), EIP155Block: params.MainNetSpuriousDragon.Int64(),
EIP158Block: params.MainNetSpuriousDragon.Int64(), EIP158Block: params.MainNetSpuriousDragon.Int64(),
PoWConfig: new(PoWConfig),
} }
} }
@ -58,7 +57,6 @@ func TestnetChainConfig() *ChainConfig {
EIP150Hash: Hash{params.TestNetHomesteadGasRepriceHash}, EIP150Hash: Hash{params.TestNetHomesteadGasRepriceHash},
EIP155Block: params.TestNetSpuriousDragon.Int64(), EIP155Block: params.TestNetSpuriousDragon.Int64(),
EIP158Block: params.TestNetSpuriousDragon.Int64(), EIP158Block: params.TestNetSpuriousDragon.Int64(),
PoWConfig: new(PoWConfig),
} }
} }
@ -73,25 +71,14 @@ func TestnetGenesis() string {
// ChainConfig is the core config which determines the blockchain settings. // ChainConfig is the core config which determines the blockchain settings.
type ChainConfig struct { type ChainConfig struct {
ChainID int64 // Chain ID for replay protection ChainID int64 // Chain ID for replay protection
HomesteadBlock int64 // Homestead switch block HomesteadBlock int64 // Homestead switch block
DAOForkBlock int64 // TheDAO hard-fork switch block DAOForkBlock int64 // TheDAO hard-fork switch block
DAOForkSupport bool // Whether the nodes supports or opposes the DAO hard-fork DAOForkSupport bool // Whether the nodes supports or opposes the DAO hard-fork
EIP150Block int64 // Homestead gas reprice switch block EIP150Block int64 // Homestead gas reprice switch block
EIP150Hash Hash // Homestead gas reprice switch block hash EIP150Hash Hash // Homestead gas reprice switch block hash
EIP155Block int64 // Replay protection switch block EIP155Block int64 // Replay protection switch block
EIP158Block int64 // Empty account pruning switch block EIP158Block int64 // Empty account pruning switch block
PoWConfig *PoWConfig // Consensus rules for the proof-of-work engine
PoAConfig *PoAConfig // Consensus rules for the proof-of-authority engine
}
// PoWConfig is the consensus engine configs for proof-of-work based sealing.
type PoWConfig struct{}
// PoAConfig is the consensus engine configs for proof-of-authorization based
// sealing.
type PoAConfig struct {
Signers Addresses
} }
// NewChainConfig creates a new chain configuration that transitions immediately // NewChainConfig creates a new chain configuration that transitions immediately