forked from ethereum/go-ethereum
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Revert "miner: suspend miner if node is syncing (ethereum#27218)"
This reverts commit f768ce5.
- Loading branch information
Showing
8 changed files
with
650 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
// 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 miner | ||
|
||
import ( | ||
"container/ring" | ||
"sync" | ||
|
||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/ethereum/go-ethereum/core/types" | ||
"github.com/ethereum/go-ethereum/log" | ||
) | ||
|
||
// chainRetriever is used by the unconfirmed block set to verify whether a previously | ||
// mined block is part of the canonical chain or not. | ||
type chainRetriever interface { | ||
// GetHeaderByNumber retrieves the canonical header associated with a block number. | ||
GetHeaderByNumber(number uint64) *types.Header | ||
|
||
// GetBlockByNumber retrieves the canonical block associated with a block number. | ||
GetBlockByNumber(number uint64) *types.Block | ||
} | ||
|
||
// unconfirmedBlock is a small collection of metadata about a locally mined block | ||
// that is placed into a unconfirmed set for canonical chain inclusion tracking. | ||
type unconfirmedBlock struct { | ||
index uint64 | ||
hash common.Hash | ||
} | ||
|
||
// unconfirmedBlocks implements a data structure to maintain locally mined blocks | ||
// have not yet reached enough maturity to guarantee chain inclusion. It is | ||
// used by the miner to provide logs to the user when a previously mined block | ||
// has a high enough guarantee to not be reorged out of the canonical chain. | ||
type unconfirmedBlocks struct { | ||
chain chainRetriever // Blockchain to verify canonical status through | ||
depth uint // Depth after which to discard previous blocks | ||
blocks *ring.Ring // Block infos to allow canonical chain cross checks | ||
lock sync.Mutex // Protects the fields from concurrent access | ||
} | ||
|
||
// newUnconfirmedBlocks returns new data structure to track currently unconfirmed blocks. | ||
func newUnconfirmedBlocks(chain chainRetriever, depth uint) *unconfirmedBlocks { | ||
return &unconfirmedBlocks{ | ||
chain: chain, | ||
depth: depth, | ||
} | ||
} | ||
|
||
// Insert adds a new block to the set of unconfirmed ones. | ||
func (set *unconfirmedBlocks) Insert(index uint64, hash common.Hash) { | ||
// If a new block was mined locally, shift out any old enough blocks | ||
set.Shift(index) | ||
|
||
// Create the new item as its own ring | ||
item := ring.New(1) | ||
item.Value = &unconfirmedBlock{ | ||
index: index, | ||
hash: hash, | ||
} | ||
// Set as the initial ring or append to the end | ||
set.lock.Lock() | ||
defer set.lock.Unlock() | ||
|
||
if set.blocks == nil { | ||
set.blocks = item | ||
} else { | ||
set.blocks.Move(-1).Link(item) | ||
} | ||
// Display a log for the user to notify of a new mined block unconfirmed | ||
log.Info("🔨 mined potential block", "number", index, "hash", hash) | ||
} | ||
|
||
// Shift drops all unconfirmed blocks from the set which exceed the unconfirmed sets depth | ||
// allowance, checking them against the canonical chain for inclusion or staleness | ||
// report. | ||
func (set *unconfirmedBlocks) Shift(height uint64) { | ||
set.lock.Lock() | ||
defer set.lock.Unlock() | ||
|
||
for set.blocks != nil { | ||
// Retrieve the next unconfirmed block and abort if too fresh | ||
next := set.blocks.Value.(*unconfirmedBlock) | ||
if next.index+uint64(set.depth) > height { | ||
break | ||
} | ||
// Block seems to exceed depth allowance, check for canonical status | ||
header := set.chain.GetHeaderByNumber(next.index) | ||
switch { | ||
case header == nil: | ||
log.Warn("Failed to retrieve header of mined block", "number", next.index, "hash", next.hash) | ||
case header.Hash() == next.hash: | ||
log.Info("🔗 block reached canonical chain", "number", next.index, "hash", next.hash) | ||
default: | ||
// Block is not canonical, check whether we have an uncle or a lost block | ||
included := false | ||
for number := next.index; !included && number < next.index+uint64(set.depth) && number <= height; number++ { | ||
if block := set.chain.GetBlockByNumber(number); block != nil { | ||
for _, uncle := range block.Uncles() { | ||
if uncle.Hash() == next.hash { | ||
included = true | ||
break | ||
} | ||
} | ||
} | ||
} | ||
if included { | ||
log.Info("⑂ block became an uncle", "number", next.index, "hash", next.hash) | ||
} else { | ||
log.Info("😱 block lost", "number", next.index, "hash", next.hash) | ||
} | ||
} | ||
// Drop the block out of the ring | ||
if set.blocks.Value == set.blocks.Next().Value { | ||
set.blocks = nil | ||
} else { | ||
set.blocks = set.blocks.Move(-1) | ||
set.blocks.Unlink(1) | ||
set.blocks = set.blocks.Move(1) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// 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 miner | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/ethereum/go-ethereum/core/types" | ||
) | ||
|
||
// noopChainRetriever is an implementation of headerRetriever that always | ||
// returns nil for any requested headers. | ||
type noopChainRetriever struct{} | ||
|
||
func (r *noopChainRetriever) GetHeaderByNumber(number uint64) *types.Header { | ||
return nil | ||
} | ||
func (r *noopChainRetriever) GetBlockByNumber(number uint64) *types.Block { | ||
return nil | ||
} | ||
|
||
// Tests that inserting blocks into the unconfirmed set accumulates them until | ||
// the desired depth is reached, after which they begin to be dropped. | ||
func TestUnconfirmedInsertBounds(t *testing.T) { | ||
limit := uint(10) | ||
|
||
pool := newUnconfirmedBlocks(new(noopChainRetriever), limit) | ||
for depth := uint64(0); depth < 2*uint64(limit); depth++ { | ||
// Insert multiple blocks for the same level just to stress it | ||
for i := 0; i < int(depth); i++ { | ||
pool.Insert(depth, [32]byte{byte(depth), byte(i)}) | ||
} | ||
// Validate that no blocks below the depth allowance are left in | ||
pool.blocks.Do(func(block interface{}) { | ||
if block := block.(*unconfirmedBlock); block.index+uint64(limit) <= depth { | ||
t.Errorf("depth %d: block %x not dropped", depth, block.hash) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
// Tests that shifting blocks out of the unconfirmed set works both for normal | ||
// cases as well as for corner cases such as empty sets, empty shifts or full | ||
// shifts. | ||
func TestUnconfirmedShifts(t *testing.T) { | ||
// Create a pool with a few blocks on various depths | ||
limit, start := uint(10), uint64(25) | ||
|
||
pool := newUnconfirmedBlocks(new(noopChainRetriever), limit) | ||
for depth := start; depth < start+uint64(limit); depth++ { | ||
pool.Insert(depth, [32]byte{byte(depth)}) | ||
} | ||
// Try to shift below the limit and ensure no blocks are dropped | ||
pool.Shift(start + uint64(limit) - 1) | ||
if n := pool.blocks.Len(); n != int(limit) { | ||
t.Errorf("unconfirmed count mismatch: have %d, want %d", n, limit) | ||
} | ||
// Try to shift half the blocks out and verify remainder | ||
pool.Shift(start + uint64(limit) - 1 + uint64(limit/2)) | ||
if n := pool.blocks.Len(); n != int(limit)/2 { | ||
t.Errorf("unconfirmed count mismatch: have %d, want %d", n, limit/2) | ||
} | ||
// Try to shift all the remaining blocks out and verify emptiness | ||
pool.Shift(start + 2*uint64(limit)) | ||
if n := pool.blocks.Len(); n != 0 { | ||
t.Errorf("unconfirmed count mismatch: have %d, want %d", n, 0) | ||
} | ||
// Try to shift out from the empty set and make sure it doesn't break | ||
pool.Shift(start + 3*uint64(limit)) | ||
if n := pool.blocks.Len(); n != 0 { | ||
t.Errorf("unconfirmed count mismatch: have %d, want %d", n, 0) | ||
} | ||
} |
Oops, something went wrong.