aboutsummaryrefslogtreecommitdiffstats
path: root/core/headerchain.go
diff options
context:
space:
mode:
authorPéter Szilágyi <peterke@gmail.com>2017-04-05 06:16:29 +0800
committerFelix Lange <fjl@users.noreply.github.com>2017-04-05 06:16:29 +0800
commit09777952ee476ff80d4b6e63b5041ff5ca0e441b (patch)
treee85320f88f548201e3476b3e7095e96fd071617b /core/headerchain.go
parente50a5b77712d891ff409aa942a5cbc24e721b332 (diff)
downloadgo-tangerine-09777952ee476ff80d4b6e63b5041ff5ca0e441b.tar.gz
go-tangerine-09777952ee476ff80d4b6e63b5041ff5ca0e441b.tar.zst
go-tangerine-09777952ee476ff80d4b6e63b5041ff5ca0e441b.zip
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.
Diffstat (limited to 'core/headerchain.go')
-rw-r--r--core/headerchain.go149
1 files changed, 37 insertions, 112 deletions
diff --git a/core/headerchain.go b/core/headerchain.go
index e7660cc60..e2d0ff5b1 100644
--- a/core/headerchain.go
+++ b/core/headerchain.go
@@ -18,21 +18,19 @@ package core
import (
crand "crypto/rand"
+ "errors"
"fmt"
"math"
"math/big"
mrand "math/rand"
- "runtime"
- "sync"
- "sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/pow"
"github.com/hashicorp/golang-lru"
)
@@ -62,18 +60,15 @@ type HeaderChain struct {
procInterrupt func() bool
- rand *mrand.Rand
- getValidator getHeaderValidatorFn
+ rand *mrand.Rand
+ engine consensus.Engine
}
-// getHeaderValidatorFn returns a HeaderValidator interface
-type getHeaderValidatorFn func() HeaderValidator
-
// NewHeaderChain creates a new HeaderChain structure.
// getValidator should return the parent's validator
// procInterrupt points to the parent's interrupt semaphore
// wg points to the parent's shutdown wait group
-func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) {
+func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine consensus.Engine, procInterrupt func() bool) (*HeaderChain, error) {
headerCache, _ := lru.New(headerCacheLimit)
tdCache, _ := lru.New(tdCacheLimit)
numberCache, _ := lru.New(numberCacheLimit)
@@ -92,7 +87,7 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, getValid
numberCache: numberCache,
procInterrupt: procInterrupt,
rand: mrand.New(mrand.NewSource(seed.Int64())),
- getValidator: getValidator,
+ engine: engine,
}
hc.genesisHeader = hc.GetHeaderByNumber(0)
@@ -228,78 +223,34 @@ func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int)
}
}
- // Generate the list of headers that should be POW verified
- verify := make([]bool, len(chain))
- for i := 0; i < len(verify)/checkFreq; i++ {
+ // Generate the list of seal verification requests, and start the parallel verifier
+ seals := make([]bool, len(chain))
+ for i := 0; i < len(seals)/checkFreq; i++ {
index := i*checkFreq + hc.rand.Intn(checkFreq)
- if index >= len(verify) {
- index = len(verify) - 1
+ if index >= len(seals) {
+ index = len(seals) - 1
}
- verify[index] = true
- }
- verify[len(verify)-1] = true // Last should always be verified to avoid junk
-
- // Create the header verification task queue and worker functions
- tasks := make(chan int, len(chain))
- for i := 0; i < len(chain); i++ {
- tasks <- i
+ seals[index] = true
}
- close(tasks)
+ seals[len(seals)-1] = true // Last should always be verified to avoid junk
- errs, failed := make([]error, len(tasks)), int32(0)
- process := func(worker int) {
- for index := range tasks {
- header, hash := chain[index], chain[index].Hash()
+ abort, results := hc.engine.VerifyHeaders(hc, chain, seals)
+ defer close(abort)
- // Short circuit insertion if shutting down or processing failed
- if hc.procInterrupt() {
- return
- }
- if atomic.LoadInt32(&failed) > 0 {
- return
- }
- // Short circuit if the header is bad or already known
- if BadHashes[hash] {
- errs[index] = BadHashError(hash)
- atomic.AddInt32(&failed, 1)
- return
- }
- if hc.HasHeader(hash) {
- continue
- }
- // Verify that the header honors the chain parameters
- checkPow := verify[index]
-
- var err error
- if index == 0 {
- err = hc.getValidator().ValidateHeader(header, hc.GetHeader(header.ParentHash, header.Number.Uint64()-1), checkPow)
- } else {
- err = hc.getValidator().ValidateHeader(header, chain[index-1], checkPow)
- }
- if err != nil {
- errs[index] = err
- atomic.AddInt32(&failed, 1)
- return
- }
+ // Iterate over the headers and ensure they all check out
+ for i, header := range chain {
+ // If the chain is terminating, stop processing blocks
+ if hc.procInterrupt() {
+ log.Debug("Premature abort during headers verification")
+ return 0, errors.New("aborted")
}
- }
- // Start as many worker threads as goroutines allowed
- pending := new(sync.WaitGroup)
- for i := 0; i < runtime.GOMAXPROCS(0); i++ {
- pending.Add(1)
- go func(id int) {
- defer pending.Done()
- process(id)
- }(i)
- }
- pending.Wait()
-
- // If anything failed, report
- if failed > 0 {
- for i, err := range errs {
- if err != nil {
- return i, err
- }
+ // If the header is a banned one, straight out abort
+ if BadHashes[header.Hash()] {
+ return i, BadHashError(header.Hash())
+ }
+ // Otherwise wait for headers checks and ensure they pass
+ if err := <-results; err != nil {
+ return i, err
}
}
@@ -313,13 +264,11 @@ func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, writeHeader WhCa
for i, header := range chain {
// Short circuit insertion if shutting down
if hc.procInterrupt() {
- log.Debug("Premature abort during headers processing")
- break
+ log.Debug("Premature abort during headers import")
+ return i, errors.New("aborted")
}
- hash := header.Hash()
-
// If the header's already known, skip it, otherwise store
- if hc.HasHeader(hash) {
+ if hc.GetHeader(header.Hash(), header.Number.Uint64()) != nil {
stats.ignored++
continue
}
@@ -490,35 +439,11 @@ func (hc *HeaderChain) SetGenesis(head *types.Header) {
hc.genesisHeader = head
}
-// headerValidator is responsible for validating block headers
-//
-// headerValidator implements HeaderValidator.
-type headerValidator struct {
- config *params.ChainConfig
- hc *HeaderChain // Canonical header chain
- Pow pow.PoW // Proof of work used for validating
-}
-
-// NewBlockValidator returns a new block validator which is safe for re-use
-func NewHeaderValidator(config *params.ChainConfig, chain *HeaderChain, pow pow.PoW) HeaderValidator {
- return &headerValidator{
- config: config,
- Pow: pow,
- hc: chain,
- }
-}
+// Config retrieves the header chain's chain configuration.
+func (hc *HeaderChain) Config() *params.ChainConfig { return hc.config }
-// ValidateHeader validates the given header and, depending on the pow arg,
-// checks the proof of work of the given header. Returns an error if the
-// validation failed.
-func (v *headerValidator) ValidateHeader(header, parent *types.Header, checkPow bool) error {
- // Short circuit if the parent is missing.
- if parent == nil {
- return ParentError(header.ParentHash)
- }
- // Short circuit if the header's already known or its parent missing
- if v.hc.HasHeader(header.Hash()) {
- return nil
- }
- return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false)
+// GetBlock implements consensus.ChainReader, and returns nil for every input as
+// a header chain does not have blocks available for retrieval.
+func (hc *HeaderChain) GetBlock(hash common.Hash, number uint64) *types.Block {
+ return nil
}