aboutsummaryrefslogtreecommitdiffstats
path: root/consensus
diff options
context:
space:
mode:
authorYondon Fu <yondon.fu@gmail.com>2017-12-19 06:17:41 +0800
committerYondon Fu <yondon.fu@gmail.com>2017-12-19 06:17:41 +0800
commit3857cdc267e3192697f561df0a0f827f65dfb6b5 (patch)
tree401c52c4972a68229ea283a394a0b0a5f3cfdc8e /consensus
parenta5330fe0c569b75cb8a524f60f7e8dc06498262b (diff)
parentfe070ab5c32702033489f1b9d1655ea1b894c29e (diff)
downloaddexon-3857cdc267e3192697f561df0a0f827f65dfb6b5.tar.gz
dexon-3857cdc267e3192697f561df0a0f827f65dfb6b5.tar.zst
dexon-3857cdc267e3192697f561df0a0f827f65dfb6b5.zip
Merge branch 'master' into abi-offset-fixed-arrays
Diffstat (limited to 'consensus')
-rw-r--r--consensus/clique/clique.go14
-rw-r--r--consensus/clique/snapshot_test.go2
-rw-r--r--consensus/ethash/algorithm.go6
-rw-r--r--consensus/ethash/algorithm_go1.8.go4
-rw-r--r--consensus/ethash/algorithm_test.go3
-rw-r--r--consensus/ethash/consensus.go23
-rw-r--r--consensus/ethash/ethash.go117
-rw-r--r--consensus/ethash/sealer.go2
8 files changed, 103 insertions, 68 deletions
diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go
index 8d6cf653d..a98058141 100644
--- a/consensus/clique/clique.go
+++ b/consensus/clique/clique.go
@@ -125,6 +125,11 @@ var (
// errUnauthorized is returned if a header is signed by a non-authorized entity.
errUnauthorized = errors.New("unauthorized")
+
+ // errWaitTransactions is returned if an empty block is attempted to be sealed
+ // on an instant chain (0 second period). It's important to refuse these as the
+ // block reward is zero, so an empty block just bloats the chain... fast.
+ errWaitTransactions = errors.New("waiting for transactions")
)
// SignerFn is a signer callback function to request a hash to be signed by a
@@ -211,9 +216,6 @@ func New(config *params.CliqueConfig, db ethdb.Database) *Clique {
if conf.Epoch == 0 {
conf.Epoch = epochLength
}
- if conf.Period == 0 {
- conf.Period = blockPeriod
- }
// Allocate the snapshot caches and create the engine
recents, _ := lru.NewARC(inmemorySnapshots)
signatures, _ := lru.NewARC(inmemorySignatures)
@@ -599,6 +601,10 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, stop <-ch
if number == 0 {
return nil, errUnknownBlock
}
+ // For 0-period chains, refuse to seal empty blocks (no reward but would spin sealing)
+ if c.config.Period == 0 && len(block.Transactions()) == 0 {
+ return nil, errWaitTransactions
+ }
// Don't hold the signer fields for the entire sealing procedure
c.lock.RLock()
signer, signFn := c.signer, c.signFn
@@ -624,7 +630,7 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, stop <-ch
}
}
// Sweet, the protocol permits us to sign the block, wait for our time
- delay := time.Unix(header.Time.Int64(), 0).Sub(time.Now())
+ delay := time.Unix(header.Time.Int64(), 0).Sub(time.Now()) // nolint: gosimple
if header.Difficulty.Cmp(diffNoTurn) == 0 {
// It's not our turn explicitly to sign, delay it a bit
wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime
diff --git a/consensus/clique/snapshot_test.go b/consensus/clique/snapshot_test.go
index a1717d799..8b51e6e09 100644
--- a/consensus/clique/snapshot_test.go
+++ b/consensus/clique/snapshot_test.go
@@ -74,7 +74,7 @@ type testerChainReader struct {
db ethdb.Database
}
-func (r *testerChainReader) Config() *params.ChainConfig { return params.AllProtocolChanges }
+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") }
diff --git a/consensus/ethash/algorithm.go b/consensus/ethash/algorithm.go
index a737bc636..76f19252f 100644
--- a/consensus/ethash/algorithm.go
+++ b/consensus/ethash/algorithm.go
@@ -102,7 +102,7 @@ func generateCache(dest []uint32, epoch uint64, seed []byte) {
header.Cap *= 4
cache := *(*[]byte)(unsafe.Pointer(&header))
- // Calculate the number of thoretical rows (we'll store in one buffer nonetheless)
+ // Calculate the number of theoretical rows (we'll store in one buffer nonetheless)
size := uint64(len(cache))
rows := int(size) / hashBytes
@@ -187,7 +187,7 @@ func fnvHash(mix []uint32, data []uint32) {
// generateDatasetItem combines data from 256 pseudorandomly selected cache nodes,
// and hashes that to compute a single dataset node.
func generateDatasetItem(cache []uint32, index uint32, keccak512 hasher) []byte {
- // Calculate the number of thoretical rows (we use one buffer nonetheless)
+ // Calculate the number of theoretical rows (we use one buffer nonetheless)
rows := uint32(len(cache) / hashWords)
// Initialize the mix
@@ -287,7 +287,7 @@ func generateDataset(dest []uint32, epoch uint64, cache []uint32) {
// hashimoto aggregates data from the full dataset in order to produce our final
// value for a particular header hash and nonce.
func hashimoto(hash []byte, nonce uint64, size uint64, lookup func(index uint32) []uint32) ([]byte, []byte) {
- // Calculate the number of thoretical rows (we use one buffer nonetheless)
+ // Calculate the number of theoretical rows (we use one buffer nonetheless)
rows := uint32(size / mixBytes)
// Combine header+nonce into a 64 byte seed
diff --git a/consensus/ethash/algorithm_go1.8.go b/consensus/ethash/algorithm_go1.8.go
index 62bf4dec1..d691b758f 100644
--- a/consensus/ethash/algorithm_go1.8.go
+++ b/consensus/ethash/algorithm_go1.8.go
@@ -31,7 +31,7 @@ func cacheSize(block uint64) uint64 {
return cacheSizes[epoch]
}
// No known cache size, calculate manually (sanity branch only)
- size := uint64(cacheInitBytes + cacheGrowthBytes*uint64(epoch) - hashBytes)
+ size := cacheInitBytes + cacheGrowthBytes*uint64(epoch) - hashBytes
for !new(big.Int).SetUint64(size / hashBytes).ProbablyPrime(1) { // Always accurate for n < 2^64
size -= 2 * hashBytes
}
@@ -49,7 +49,7 @@ func datasetSize(block uint64) uint64 {
return datasetSizes[epoch]
}
// No known dataset size, calculate manually (sanity branch only)
- size := uint64(datasetInitBytes + datasetGrowthBytes*uint64(epoch) - mixBytes)
+ size := datasetInitBytes + datasetGrowthBytes*uint64(epoch) - mixBytes
for !new(big.Int).SetUint64(size / mixBytes).ProbablyPrime(1) { // Always accurate for n < 2^64
size -= 2 * mixBytes
}
diff --git a/consensus/ethash/algorithm_test.go b/consensus/ethash/algorithm_test.go
index 7e4307a74..7765ff9fe 100644
--- a/consensus/ethash/algorithm_test.go
+++ b/consensus/ethash/algorithm_test.go
@@ -703,8 +703,7 @@ func TestConcurrentDiskCacheGeneration(t *testing.T) {
go func(idx int) {
defer pend.Done()
-
- ethash := New(cachedir, 0, 1, "", 0, 0)
+ ethash := New(Config{cachedir, 0, 1, "", 0, 0, ModeNormal})
if err := ethash.VerifySeal(nil, block.Header()); err != nil {
t.Errorf("proc %d: block verification failed: %v", idx, err)
}
diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go
index 6a19d449f..2bdb18677 100644
--- a/consensus/ethash/consensus.go
+++ b/consensus/ethash/consensus.go
@@ -36,9 +36,10 @@ import (
// Ethash proof-of-work protocol constants.
var (
- frontierBlockReward *big.Int = big.NewInt(5e+18) // Block reward in wei for successfully mining a block
- byzantiumBlockReward *big.Int = big.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Byzantium
- maxUncles = 2 // Maximum number of uncles allowed in a single block
+ FrontierBlockReward *big.Int = big.NewInt(5e+18) // Block reward in wei for successfully mining a block
+ ByzantiumBlockReward *big.Int = big.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Byzantium
+ 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
)
// Various error messages to mark blocks invalid. These should be private to
@@ -68,7 +69,7 @@ func (ethash *Ethash) Author(header *types.Header) (common.Address, error) {
// 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 {
+ if ethash.config.PowMode == ModeFullFake {
return nil
}
// Short circuit if the header is known, or it's parent not
@@ -89,7 +90,7 @@ func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.He
// 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 || len(headers) == 0 {
+ if ethash.config.PowMode == ModeFullFake || len(headers) == 0 {
abort, results := make(chan struct{}), make(chan error, len(headers))
for i := 0; i < len(headers); i++ {
results <- nil
@@ -169,7 +170,7 @@ func (ethash *Ethash) verifyHeaderWorker(chain consensus.ChainReader, headers []
// 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 {
+ if ethash.config.PowMode == ModeFullFake {
return nil
}
// Verify that there are at most 2 uncles included in this block
@@ -231,7 +232,7 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *
return errLargeBlockTime
}
} else {
- if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 {
+ if header.Time.Cmp(big.NewInt(time.Now().Add(allowedFutureBlockTime).Unix())) > 0 {
return consensus.ErrFutureBlock
}
}
@@ -455,7 +456,7 @@ func calcDifficultyFrontier(time uint64, parent *types.Header) *big.Int {
// 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 {
+ if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake {
time.Sleep(ethash.fakeDelay)
if ethash.fakeFail == header.Number.Uint64() {
return errInvalidPoW
@@ -480,7 +481,7 @@ func (ethash *Ethash) VerifySeal(chain consensus.ChainReader, header *types.Head
cache := ethash.cache(number)
size := datasetSize(number)
- if ethash.tester {
+ if ethash.config.PowMode == ModeTest {
size = 32 * 1024
}
digest, result := hashimotoLight(size, cache, header.HashNoNonce().Bytes(), header.Nonce.Uint64())
@@ -529,9 +530,9 @@ var (
// TODO (karalabe): Move the chain maker into this package and make this private!
func AccumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) {
// Select the correct block reward based on chain progression
- blockReward := frontierBlockReward
+ blockReward := FrontierBlockReward
if config.IsByzantium(header.Number) {
- blockReward = byzantiumBlockReward
+ blockReward = ByzantiumBlockReward
}
// Accumulate the rewards for the miner and any included uncles
reward := new(big.Int).Set(blockReward)
diff --git a/consensus/ethash/ethash.go b/consensus/ethash/ethash.go
index dd6147072..a78b3a895 100644
--- a/consensus/ethash/ethash.go
+++ b/consensus/ethash/ethash.go
@@ -45,7 +45,7 @@ var (
maxUint256 = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0))
// sharedEthash is a full instance that can be shared between multiple users.
- sharedEthash = New("", 3, 0, "", 1, 0)
+ sharedEthash = New(Config{"", 3, 0, "", 1, 0, ModeNormal})
// algorithmRevision is the data structure version used for file naming.
algorithmRevision = 23
@@ -320,15 +320,32 @@ func MakeDataset(block uint64, dir string) {
d.release()
}
+// Mode defines the type and amount of PoW verification an ethash engine makes.
+type Mode uint
+
+const (
+ ModeNormal Mode = iota
+ ModeShared
+ ModeTest
+ ModeFake
+ ModeFullFake
+)
+
+// Config are the configuration parameters of the ethash.
+type Config struct {
+ CacheDir string
+ CachesInMem int
+ CachesOnDisk int
+ DatasetDir string
+ DatasetsInMem int
+ DatasetsOnDisk int
+ PowMode Mode
+}
+
// Ethash is a consensus engine based on proot-of-work implementing the ethash
// algorithm.
type Ethash struct {
- cachedir string // Data directory to store the verification caches
- cachesinmem int // Number of caches to keep in memory
- cachesondisk int // Number of caches to keep on disk
- dagdir string // Data directory to store full mining datasets
- dagsinmem int // Number of mining datasets to keep in memory
- dagsondisk int // Number of mining datasets to keep on disk
+ config Config
caches map[uint64]*cache // In memory caches to avoid regenerating too often
fcache *cache // Pre-generated cache for the estimated future epoch
@@ -342,10 +359,7 @@ type Ethash struct {
hashrate metrics.Meter // Meter tracking the average hashrate
// The fields below are hooks for testing
- tester bool // Flag whether to use a smaller test dataset
shared *Ethash // Shared PoW verifier to avoid cache regeneration
- fakeMode bool // Flag whether to disable PoW checking
- fakeFull bool // Flag whether to disable all consensus rules
fakeFail uint64 // Block number which fails PoW check even in fake mode
fakeDelay time.Duration // Time delay to sleep for before returning from verify
@@ -353,28 +367,23 @@ type Ethash struct {
}
// New creates a full sized ethash PoW scheme.
-func New(cachedir string, cachesinmem, cachesondisk int, dagdir string, dagsinmem, dagsondisk int) *Ethash {
- if cachesinmem <= 0 {
- log.Warn("One ethash cache must always be in memory", "requested", cachesinmem)
- cachesinmem = 1
+func New(config Config) *Ethash {
+ if config.CachesInMem <= 0 {
+ log.Warn("One ethash cache must always be in memory", "requested", config.CachesInMem)
+ config.CachesInMem = 1
}
- if cachedir != "" && cachesondisk > 0 {
- log.Info("Disk storage enabled for ethash caches", "dir", cachedir, "count", cachesondisk)
+ if config.CacheDir != "" && config.CachesOnDisk > 0 {
+ log.Info("Disk storage enabled for ethash caches", "dir", config.CacheDir, "count", config.CachesOnDisk)
}
- if dagdir != "" && dagsondisk > 0 {
- log.Info("Disk storage enabled for ethash DAGs", "dir", dagdir, "count", dagsondisk)
+ if config.DatasetDir != "" && config.DatasetsOnDisk > 0 {
+ log.Info("Disk storage enabled for ethash DAGs", "dir", config.DatasetDir, "count", config.DatasetsOnDisk)
}
return &Ethash{
- cachedir: cachedir,
- cachesinmem: cachesinmem,
- cachesondisk: cachesondisk,
- dagdir: dagdir,
- dagsinmem: dagsinmem,
- dagsondisk: dagsondisk,
- caches: make(map[uint64]*cache),
- datasets: make(map[uint64]*dataset),
- update: make(chan struct{}),
- hashrate: metrics.NewMeter(),
+ config: config,
+ caches: make(map[uint64]*cache),
+ datasets: make(map[uint64]*dataset),
+ update: make(chan struct{}),
+ hashrate: metrics.NewMeter(),
}
}
@@ -382,12 +391,14 @@ func New(cachedir string, cachesinmem, cachesondisk int, dagdir string, dagsinme
// purposes.
func NewTester() *Ethash {
return &Ethash{
- cachesinmem: 1,
- caches: make(map[uint64]*cache),
- datasets: make(map[uint64]*dataset),
- tester: true,
- update: make(chan struct{}),
- hashrate: metrics.NewMeter(),
+ config: Config{
+ CachesInMem: 1,
+ PowMode: ModeTest,
+ },
+ caches: make(map[uint64]*cache),
+ datasets: make(map[uint64]*dataset),
+ update: make(chan struct{}),
+ hashrate: metrics.NewMeter(),
}
}
@@ -395,27 +406,45 @@ func NewTester() *Ethash {
// all blocks' seal as valid, though they still have to conform to the Ethereum
// consensus rules.
func NewFaker() *Ethash {
- return &Ethash{fakeMode: true}
+ return &Ethash{
+ config: Config{
+ PowMode: ModeFake,
+ },
+ }
}
// NewFakeFailer creates a ethash consensus engine with a fake PoW scheme that
// accepts all blocks as valid apart from the single one specified, though they
// still have to conform to the Ethereum consensus rules.
func NewFakeFailer(fail uint64) *Ethash {
- return &Ethash{fakeMode: true, fakeFail: fail}
+ return &Ethash{
+ config: Config{
+ PowMode: ModeFake,
+ },
+ fakeFail: fail,
+ }
}
// NewFakeDelayer creates a ethash consensus engine with a fake PoW scheme that
// accepts all blocks as valid, but delays verifications by some time, though
// they still have to conform to the Ethereum consensus rules.
func NewFakeDelayer(delay time.Duration) *Ethash {
- return &Ethash{fakeMode: true, fakeDelay: delay}
+ return &Ethash{
+ config: Config{
+ PowMode: ModeFake,
+ },
+ fakeDelay: delay,
+ }
}
// NewFullFaker creates an ethash consensus engine with a full fake scheme that
// accepts all blocks as valid, without checking any consensus rules whatsoever.
func NewFullFaker() *Ethash {
- return &Ethash{fakeMode: true, fakeFull: true}
+ return &Ethash{
+ config: Config{
+ PowMode: ModeFullFake,
+ },
+ }
}
// NewShared creates a full sized ethash PoW shared between all requesters running
@@ -436,7 +465,7 @@ func (ethash *Ethash) cache(block uint64) []uint32 {
current, future := ethash.caches[epoch], (*cache)(nil)
if current == nil {
// No in-memory cache, evict the oldest if the cache limit was reached
- for len(ethash.caches) > 0 && len(ethash.caches) >= ethash.cachesinmem {
+ for len(ethash.caches) > 0 && len(ethash.caches) >= ethash.config.CachesInMem {
var evict *cache
for _, cache := range ethash.caches {
if evict == nil || evict.used.After(cache.used) {
@@ -473,7 +502,7 @@ func (ethash *Ethash) cache(block uint64) []uint32 {
ethash.lock.Unlock()
// Wait for generation finish, bump the timestamp and finalize the cache
- current.generate(ethash.cachedir, ethash.cachesondisk, ethash.tester)
+ current.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest)
current.lock.Lock()
current.used = time.Now()
@@ -481,7 +510,7 @@ func (ethash *Ethash) cache(block uint64) []uint32 {
// If we exhausted the future cache, now's a good time to regenerate it
if future != nil {
- go future.generate(ethash.cachedir, ethash.cachesondisk, ethash.tester)
+ go future.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest)
}
return current.cache
}
@@ -498,7 +527,7 @@ func (ethash *Ethash) dataset(block uint64) []uint32 {
current, future := ethash.datasets[epoch], (*dataset)(nil)
if current == nil {
// No in-memory dataset, evict the oldest if the dataset limit was reached
- for len(ethash.datasets) > 0 && len(ethash.datasets) >= ethash.dagsinmem {
+ for len(ethash.datasets) > 0 && len(ethash.datasets) >= ethash.config.DatasetsInMem {
var evict *dataset
for _, dataset := range ethash.datasets {
if evict == nil || evict.used.After(dataset.used) {
@@ -536,7 +565,7 @@ func (ethash *Ethash) dataset(block uint64) []uint32 {
ethash.lock.Unlock()
// Wait for generation finish, bump the timestamp and finalize the cache
- current.generate(ethash.dagdir, ethash.dagsondisk, ethash.tester)
+ current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest)
current.lock.Lock()
current.used = time.Now()
@@ -544,7 +573,7 @@ func (ethash *Ethash) dataset(block uint64) []uint32 {
// If we exhausted the future dataset, now's a good time to regenerate it
if future != nil {
- go future.generate(ethash.dagdir, ethash.dagsondisk, ethash.tester)
+ go future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest)
}
return current.dataset
}
diff --git a/consensus/ethash/sealer.go b/consensus/ethash/sealer.go
index 784e8f649..c2447e473 100644
--- a/consensus/ethash/sealer.go
+++ b/consensus/ethash/sealer.go
@@ -34,7 +34,7 @@ import (
// 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 {
+ if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake {
header := block.Header()
header.Nonce, header.MixDigest = types.BlockNonce{}, common.Hash{}
return block.WithSeal(header), nil