diff options
author | Yondon Fu <yondon.fu@gmail.com> | 2017-12-19 06:17:41 +0800 |
---|---|---|
committer | Yondon Fu <yondon.fu@gmail.com> | 2017-12-19 06:17:41 +0800 |
commit | 3857cdc267e3192697f561df0a0f827f65dfb6b5 (patch) | |
tree | 401c52c4972a68229ea283a394a0b0a5f3cfdc8e /consensus | |
parent | a5330fe0c569b75cb8a524f60f7e8dc06498262b (diff) | |
parent | fe070ab5c32702033489f1b9d1655ea1b894c29e (diff) | |
download | dexon-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.go | 14 | ||||
-rw-r--r-- | consensus/clique/snapshot_test.go | 2 | ||||
-rw-r--r-- | consensus/ethash/algorithm.go | 6 | ||||
-rw-r--r-- | consensus/ethash/algorithm_go1.8.go | 4 | ||||
-rw-r--r-- | consensus/ethash/algorithm_test.go | 3 | ||||
-rw-r--r-- | consensus/ethash/consensus.go | 23 | ||||
-rw-r--r-- | consensus/ethash/ethash.go | 117 | ||||
-rw-r--r-- | consensus/ethash/sealer.go | 2 |
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 |