diff --git a/eth/api_backend.go b/eth/api_backend.go
index c9ad4311e..927dcdef1 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -68,6 +68,9 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb
// Pending block is only known by the miner
if number == rpc.PendingBlockNumber {
block := b.eth.miner.PendingBlock()
+ if block == nil {
+ return nil, errors.New("pending block is not available")
+ }
return block.Header(), nil
}
// Otherwise resolve and return the block
@@ -122,6 +125,9 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumbe
// Pending block is only known by the miner
if number == rpc.PendingBlockNumber {
block := b.eth.miner.PendingBlock()
+ if block == nil {
+ return nil, errors.New("pending block is not available")
+ }
return block, nil
}
// Otherwise resolve and return the block
@@ -196,6 +202,9 @@ func (b *EthAPIBackend) StateAndHeaderByNumber(ctx context.Context, number rpc.B
// Pending state is only known by the miner
if number == rpc.PendingBlockNumber {
block, state := b.eth.miner.Pending()
+ if block == nil || state == nil {
+ return nil, nil, errors.New("pending state is not available")
+ }
return state, block.Header(), nil
}
// Otherwise resolve the block number and return its state
diff --git a/eth/api_debug.go b/eth/api_debug.go
index 929e0460f..7cf023917 100644
--- a/eth/api_debug.go
+++ b/eth/api_debug.go
@@ -56,6 +56,9 @@ func (api *DebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) {
// both the pending block as well as the pending state from
// the miner and operate on those
_, stateDb := api.eth.miner.Pending()
+ if stateDb == nil {
+ return state.Dump{}, errors.New("pending state is not available")
+ }
return stateDb.RawDump(opts), nil
}
var header *types.Header
@@ -141,6 +144,9 @@ func (api *DebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start hex
// both the pending block as well as the pending state from
// the miner and operate on those
_, stateDb = api.eth.miner.Pending()
+ if stateDb == nil {
+ return state.IteratorDump{}, errors.New("pending state is not available")
+ }
} else {
var header *types.Header
if number == rpc.LatestBlockNumber {
diff --git a/eth/filters/filter.go b/eth/filters/filter.go
index 7543c0603..d65bbfacf 100644
--- a/eth/filters/filter.go
+++ b/eth/filters/filter.go
@@ -334,7 +334,7 @@ func (f *Filter) checkMatches(ctx context.Context, header *types.Header) ([]*typ
// pendingLogs returns the logs matching the filter criteria within the pending block.
func (f *Filter) pendingLogs() []*types.Log {
block, receipts := f.sys.backend.PendingBlockAndReceipts()
- if block == nil {
+ if block == nil || receipts == nil {
return nil
}
if bloomFilter(block.Bloom(), f.addresses, f.topics) {
diff --git a/miner/miner.go b/miner/miner.go
index b1d1f7c4c..b7273948f 100644
--- a/miner/miner.go
+++ b/miner/miner.go
@@ -131,16 +131,22 @@ func (miner *Miner) update() {
shouldStart = true
log.Info("Mining aborted due to sync")
}
+ miner.worker.syncing.Store(true)
+
case downloader.FailedEvent:
canStart = true
if shouldStart {
miner.worker.start()
}
+ miner.worker.syncing.Store(false)
+
case downloader.DoneEvent:
canStart = true
if shouldStart {
miner.worker.start()
}
+ miner.worker.syncing.Store(false)
+
// Stop reacting to downloader events
events.Unsubscribe()
}
@@ -196,12 +202,14 @@ func (miner *Miner) SetRecommitInterval(interval time.Duration) {
miner.worker.setRecommitInterval(interval)
}
-// Pending returns the currently pending block and associated state.
+// Pending returns the currently pending block and associated state. The returned
+// values can be nil in case the pending block is not initialized
func (miner *Miner) Pending() (*types.Block, *state.StateDB) {
return miner.worker.pending()
}
-// PendingBlock returns the currently pending block.
+// PendingBlock returns the currently pending block. The returned block can be
+// nil in case the pending block is not initialized.
//
// Note, to access both the pending block and the pending state
// simultaneously, please use Pending(), as the pending state can
@@ -211,6 +219,7 @@ func (miner *Miner) PendingBlock() *types.Block {
}
// PendingBlockAndReceipts returns the currently pending block and corresponding receipts.
+// The returned values can be nil in case the pending block is not initialized.
func (miner *Miner) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
return miner.worker.pendingBlockAndReceipts()
}
@@ -225,23 +234,6 @@ func (miner *Miner) SetGasCeil(ceil uint64) {
miner.worker.setGasCeil(ceil)
}
-// EnablePreseal turns on the preseal mining feature. It's enabled by default.
-// Note this function shouldn't be exposed to API, it's unnecessary for users
-// (miners) to actually know the underlying detail. It's only for outside project
-// which uses this library.
-func (miner *Miner) EnablePreseal() {
- miner.worker.enablePreseal()
-}
-
-// DisablePreseal turns off the preseal mining feature. It's necessary for some
-// fake consensus engine which can seal blocks instantaneously.
-// Note this function shouldn't be exposed to API, it's unnecessary for users
-// (miners) to actually know the underlying detail. It's only for outside project
-// which uses this library.
-func (miner *Miner) DisablePreseal() {
- miner.worker.disablePreseal()
-}
-
// SubscribePendingLogs starts delivering logs from pending transactions
// to the given channel.
func (miner *Miner) SubscribePendingLogs(ch chan<- []*types.Log) event.Subscription {
diff --git a/miner/unconfirmed.go b/miner/unconfirmed.go
deleted file mode 100644
index 0489f1ea4..000000000
--- a/miner/unconfirmed.go
+++ /dev/null
@@ -1,136 +0,0 @@
-// 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 .
-
-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)
- }
- }
-}
diff --git a/miner/unconfirmed_test.go b/miner/unconfirmed_test.go
deleted file mode 100644
index 60958f658..000000000
--- a/miner/unconfirmed_test.go
+++ /dev/null
@@ -1,87 +0,0 @@
-// 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 .
-
-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)
- }
-}
diff --git a/miner/worker.go b/miner/worker.go
index 936a9e74a..d61f8dcad 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -24,7 +24,6 @@ import (
"sync/atomic"
"time"
- mapset "github.com/deckarep/golang-set/v2"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc"
@@ -48,15 +47,9 @@ const (
// chainHeadChanSize is the size of channel listening to ChainHeadEvent.
chainHeadChanSize = 10
- // chainSideChanSize is the size of channel listening to ChainSideEvent.
- chainSideChanSize = 10
-
// resubmitAdjustChanSize is the size of resubmitting interval adjustment channel.
resubmitAdjustChanSize = 10
- // sealingLogAtDepth is the number of confirmations before logging successful sealing.
- sealingLogAtDepth = 7
-
// minRecommitInterval is the minimal time interval to recreate the sealing block with
// any newly arrived transactions.
minRecommitInterval = 1 * time.Second
@@ -86,57 +79,36 @@ var (
// environment is the worker's current environment and holds all
// information of the sealing block generation.
type environment struct {
- signer types.Signer
-
- state *state.StateDB // apply state changes here
- ancestors mapset.Set[common.Hash] // ancestor set (used for checking uncle parent validity)
- family mapset.Set[common.Hash] // family set (used for checking uncle invalidity)
- tcount int // tx count in cycle
- gasPool *core.GasPool // available gas used to pack transactions
- coinbase common.Address
+ signer types.Signer
+ state *state.StateDB // apply state changes here
+ tcount int // tx count in cycle
+ gasPool *core.GasPool // available gas used to pack transactions
+ coinbase common.Address
header *types.Header
txs []*types.Transaction
receipts []*types.Receipt
- uncles map[common.Hash]*types.Header
}
// copy creates a deep copy of environment.
func (env *environment) copy() *environment {
cpy := &environment{
- signer: env.signer,
- state: env.state.Copy(),
- ancestors: env.ancestors.Clone(),
- family: env.family.Clone(),
- tcount: env.tcount,
- coinbase: env.coinbase,
- header: types.CopyHeader(env.header),
- receipts: copyReceipts(env.receipts),
+ signer: env.signer,
+ state: env.state.Copy(),
+ tcount: env.tcount,
+ coinbase: env.coinbase,
+ header: types.CopyHeader(env.header),
+ receipts: copyReceipts(env.receipts),
}
if env.gasPool != nil {
gasPool := *env.gasPool
cpy.gasPool = &gasPool
}
- // The content of txs and uncles are immutable, unnecessary
- // to do the expensive deep copy for them.
cpy.txs = make([]*types.Transaction, len(env.txs))
copy(cpy.txs, env.txs)
- cpy.uncles = make(map[common.Hash]*types.Header)
- for hash, uncle := range env.uncles {
- cpy.uncles[hash] = uncle
- }
return cpy
}
-// unclelist returns the contained uncles as the list format.
-func (env *environment) unclelist() []*types.Header {
- var uncles []*types.Header
- for _, uncle := range env.uncles {
- uncles = append(uncles, uncle)
- }
- return uncles
-}
-
// discard terminates the background prefetcher go-routine. It should
// always be called for all created environment instances otherwise
// the go-routine leak can happen.
@@ -165,7 +137,6 @@ const (
// newWorkReq represents a request for new sealing work submitting with relative interrupt notifier.
type newWorkReq struct {
interrupt *atomic.Int32
- noempty bool
timestamp int64
}
@@ -206,8 +177,6 @@ type worker struct {
txsSub event.Subscription
chainHeadCh chan core.ChainHeadEvent
chainHeadSub event.Subscription
- chainSideCh chan core.ChainSideEvent
- chainSideSub event.Subscription
// Channels
newWorkCh chan *newWorkReq
@@ -221,10 +190,7 @@ type worker struct {
wg sync.WaitGroup
- current *environment // An environment for current running cycle.
- localUncles map[common.Hash]*types.Block // A set of side blocks generated locally as the possible uncle blocks.
- remoteUncles map[common.Hash]*types.Block // A set of side blocks as the possible uncle blocks.
- unconfirmed *unconfirmedBlocks // A set of locally mined blocks pending canonicalness confirmations.
+ current *environment // An environment for current running cycle.
mu sync.RWMutex // The lock used to protect the coinbase and extra fields
coinbase common.Address
@@ -241,13 +207,7 @@ type worker struct {
// atomic status counters
running atomic.Bool // The indicator whether the consensus engine is running or not.
newTxs atomic.Int32 // New arrival transaction count since last sealing work submitting.
-
- // noempty is the flag used to control whether the feature of pre-seal empty
- // block is enabled. The default value is false(pre-seal is enabled by default).
- // But in some special scenario the consensus engine will seal blocks instantaneously,
- // in this case this feature will add all empty blocks into canonical chain
- // non-stop and no real transaction will be included.
- noempty atomic.Bool
+ syncing atomic.Bool // The indicator whether the node is still syncing.
// newpayloadTimeout is the maximum timeout allowance for creating payload.
// The default value is 2 seconds but node operator can set it to arbitrary
@@ -279,15 +239,11 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
chain: eth.BlockChain(),
mux: mux,
isLocalBlock: isLocalBlock,
- localUncles: make(map[common.Hash]*types.Block),
- remoteUncles: make(map[common.Hash]*types.Block),
- unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), sealingLogAtDepth),
coinbase: config.Etherbase,
extra: config.ExtraData,
pendingTasks: make(map[common.Hash]*task),
txsCh: make(chan core.NewTxsEvent, txChanSize),
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
- chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize),
newWorkCh: make(chan *newWorkReq),
getWorkCh: make(chan *getWorkReq),
taskCh: make(chan *task),
@@ -301,7 +257,6 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
worker.txsSub = eth.TxPool().SubscribeNewTxsEvent(worker.txsCh)
// Subscribe events for blockchain
worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh)
- worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh)
// Sanitize recommit interval if the user-specified one is too short.
recommit := worker.config.Recommit
@@ -370,19 +325,9 @@ func (w *worker) setRecommitInterval(interval time.Duration) {
}
}
-// disablePreseal disables pre-sealing feature
-func (w *worker) disablePreseal() {
- w.noempty.Store(true)
-}
-
-// enablePreseal enables pre-sealing feature
-func (w *worker) enablePreseal() {
- w.noempty.Store(false)
-}
-
-// pending returns the pending state and corresponding block.
+// pending returns the pending state and corresponding block. The returned
+// values can be nil in case the pending block is not initialized.
func (w *worker) pending() (*types.Block, *state.StateDB) {
- // return a snapshot to avoid contention on currentMu mutex
w.snapshotMu.RLock()
defer w.snapshotMu.RUnlock()
if w.snapshotState == nil {
@@ -391,17 +336,17 @@ func (w *worker) pending() (*types.Block, *state.StateDB) {
return w.snapshotBlock, w.snapshotState.Copy()
}
-// pendingBlock returns pending block.
+// pendingBlock returns pending block. The returned block can be nil in case the
+// pending block is not initialized.
func (w *worker) pendingBlock() *types.Block {
- // return a snapshot to avoid contention on currentMu mutex
w.snapshotMu.RLock()
defer w.snapshotMu.RUnlock()
return w.snapshotBlock
}
// pendingBlockAndReceipts returns pending block and corresponding receipts.
+// The returned values can be nil in case the pending block is not initialized.
func (w *worker) pendingBlockAndReceipts() (*types.Block, types.Receipts) {
- // return a snapshot to avoid contention on currentMu mutex
w.snapshotMu.RLock()
defer w.snapshotMu.RUnlock()
return w.snapshotBlock, w.snapshotReceipts
@@ -467,13 +412,13 @@ func (w *worker) newWorkLoop(recommit time.Duration) {
<-timer.C // discard the initial tick
// commit aborts in-flight transaction execution with given signal and resubmits a new one.
- commit := func(noempty bool, s int32) {
+ commit := func(s int32) {
if interrupt != nil {
interrupt.Store(s)
}
interrupt = new(atomic.Int32)
select {
- case w.newWorkCh <- &newWorkReq{interrupt: interrupt, noempty: noempty, timestamp: timestamp}:
+ case w.newWorkCh <- &newWorkReq{interrupt: interrupt, timestamp: timestamp}:
case <-w.exitCh:
return
}
@@ -496,12 +441,12 @@ func (w *worker) newWorkLoop(recommit time.Duration) {
case <-w.startCh:
clearPending(w.chain.CurrentBlock().Number.Uint64())
timestamp = time.Now().Unix()
- commit(false, commitInterruptNewHead)
+ commit(commitInterruptNewHead)
case head := <-w.chainHeadCh:
clearPending(head.Block.NumberU64())
timestamp = time.Now().Unix()
- commit(false, commitInterruptNewHead)
+ commit(commitInterruptNewHead)
case <-timer.C:
// If sealing is running resubmit a new work cycle periodically to pull in
@@ -512,7 +457,7 @@ func (w *worker) newWorkLoop(recommit time.Duration) {
timer.Reset(recommit)
continue
}
- commit(true, commitInterruptResubmit)
+ commit(commitInterruptResubmit)
}
case interval := <-w.resubmitIntervalCh:
@@ -558,20 +503,16 @@ func (w *worker) mainLoop() {
defer w.wg.Done()
defer w.txsSub.Unsubscribe()
defer w.chainHeadSub.Unsubscribe()
- defer w.chainSideSub.Unsubscribe()
defer func() {
if w.current != nil {
w.current.discard()
}
}()
- cleanTicker := time.NewTicker(time.Second * 10)
- defer cleanTicker.Stop()
-
for {
select {
case req := <-w.newWorkCh:
- w.commitWork(req.interrupt, req.noempty, req.timestamp)
+ w.commitWork(req.interrupt, req.timestamp)
case req := <-w.getWorkCh:
block, fees, err := w.generateWork(req.params)
@@ -580,42 +521,6 @@ func (w *worker) mainLoop() {
block: block,
fees: fees,
}
- case ev := <-w.chainSideCh:
- // Short circuit for duplicate side blocks
- if _, exist := w.localUncles[ev.Block.Hash()]; exist {
- continue
- }
- if _, exist := w.remoteUncles[ev.Block.Hash()]; exist {
- continue
- }
- // Add side block to possible uncle block set depending on the author.
- if w.isLocalBlock != nil && w.isLocalBlock(ev.Block.Header()) {
- w.localUncles[ev.Block.Hash()] = ev.Block
- } else {
- w.remoteUncles[ev.Block.Hash()] = ev.Block
- }
- // If our sealing block contains less than 2 uncle blocks,
- // add the new uncle block if valid and regenerate a new
- // sealing block for higher profit.
- if w.isRunning() && w.current != nil && len(w.current.uncles) < 2 {
- start := time.Now()
- if err := w.commitUncle(w.current, ev.Block.Header()); err == nil {
- w.commit(w.current.copy(), nil, true, start)
- }
- }
-
- case <-cleanTicker.C:
- chainHead := w.chain.CurrentBlock()
- for hash, uncle := range w.localUncles {
- if uncle.NumberU64()+staleThreshold <= chainHead.Number.Uint64() {
- delete(w.localUncles, hash)
- }
- }
- for hash, uncle := range w.remoteUncles {
- if uncle.NumberU64()+staleThreshold <= chainHead.Number.Uint64() {
- delete(w.remoteUncles, hash)
- }
- }
case ev := <-w.txsCh:
// Apply transactions to the pending state if we're not sealing
@@ -647,7 +552,7 @@ func (w *worker) mainLoop() {
// submit sealing work here since all empty submission will be rejected
// by clique. Of course the advance sealing(empty submission) is disabled.
if w.chainConfig.Clique != nil && w.chainConfig.Clique.Period == 0 {
- w.commitWork(nil, true, time.Now().Unix())
+ w.commitWork(nil, time.Now().Unix())
}
}
w.newTxs.Add(int32(len(ev.Txs)))
@@ -659,8 +564,6 @@ func (w *worker) mainLoop() {
return
case <-w.chainHeadSub.Err():
return
- case <-w.chainSideSub.Err():
- return
}
}
}
@@ -780,9 +683,6 @@ func (w *worker) resultLoop() {
// Broadcast the block and announce chain insertion event
w.mux.Post(core.NewMinedBlockEvent{Block: block})
- // Insert the block into the set of pending ones to resultLoop for confirmations
- w.unconfirmed.Insert(block.NumberU64(), block.Hash())
-
case <-w.exitCh:
return
}
@@ -801,49 +701,16 @@ func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase co
// Note the passed coinbase may be different with header.Coinbase.
env := &environment{
- signer: types.MakeSigner(w.chainConfig, header.Number, header.Time),
- state: state,
- coinbase: coinbase,
- ancestors: mapset.NewSet[common.Hash](),
- family: mapset.NewSet[common.Hash](),
- header: header,
- uncles: make(map[common.Hash]*types.Header),
- }
- // when 08 is processed ancestors contain 07 (quick block)
- for _, ancestor := range w.chain.GetBlocksFromHash(parent.Hash(), 7) {
- for _, uncle := range ancestor.Uncles() {
- env.family.Add(uncle.Hash())
- }
- env.family.Add(ancestor.Hash())
- env.ancestors.Add(ancestor.Hash())
+ signer: types.MakeSigner(w.chainConfig, header.Number, header.Time),
+ state: state,
+ coinbase: coinbase,
+ header: header,
}
// Keep track of transactions which return errors so they can be removed
env.tcount = 0
return env, nil
}
-// commitUncle adds the given block to uncle block set, returns error if failed to add.
-func (w *worker) commitUncle(env *environment, uncle *types.Header) error {
- if w.isTTDReached(env.header) {
- return errors.New("ignore uncle for beacon block")
- }
- hash := uncle.Hash()
- if _, exist := env.uncles[hash]; exist {
- return errors.New("uncle not unique")
- }
- if env.header.ParentHash == uncle.ParentHash {
- return errors.New("uncle is sibling")
- }
- if !env.ancestors.Contains(uncle.ParentHash) {
- return errors.New("uncle's parent unknown")
- }
- if env.family.Contains(hash) {
- return errors.New("uncle already included")
- }
- env.uncles[hash] = uncle
- return nil
-}
-
// updateSnapshot updates pending snapshot block, receipts and state.
func (w *worker) updateSnapshot(env *environment) {
w.snapshotMu.Lock()
@@ -852,7 +719,7 @@ func (w *worker) updateSnapshot(env *environment) {
w.snapshotBlock = types.NewBlock(
env.header,
env.txs,
- env.unclelist(),
+ nil,
env.receipts,
trie.NewStackTrie(nil),
)
@@ -962,7 +829,6 @@ type generateParams struct {
coinbase common.Address // The fee recipient address for including transaction
random common.Hash // The randomness generated by beacon chain, empty before the merge
withdrawals types.Withdrawals // List of withdrawals to include in block.
- noUncle bool // Flag whether the uncle block inclusion is allowed
noTxs bool // Flag whether an empty block without any transaction is expected
}
@@ -1028,24 +894,6 @@ func (w *worker) prepareWork(genParams *generateParams) (*environment, error) {
log.Error("Failed to create sealing context", "err", err)
return nil, err
}
- // Accumulate the uncles for the sealing work only if it's allowed.
- if !genParams.noUncle {
- commitUncles := func(blocks map[common.Hash]*types.Block) {
- for hash, uncle := range blocks {
- if len(env.uncles) == 2 {
- break
- }
- if err := w.commitUncle(env, uncle.Header()); err != nil {
- log.Trace("Possible uncle rejected", "hash", hash, "reason", err)
- } else {
- log.Debug("Committing new uncle to block", "hash", hash)
- }
- }
- }
- // Prefer to locally generated uncle
- commitUncles(w.localUncles)
- commitUncles(w.remoteUncles)
- }
return env, nil
}
@@ -1098,7 +946,7 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e
log.Warn("Block building is interrupted", "allowance", common.PrettyDuration(w.newpayloadTimeout))
}
}
- block, err := w.engine.FinalizeAndAssemble(w.chain, work.header, work.state, work.txs, work.unclelist(), work.receipts, params.withdrawals)
+ block, err := w.engine.FinalizeAndAssemble(w.chain, work.header, work.state, work.txs, nil, work.receipts, params.withdrawals)
if err != nil {
return nil, nil, err
}
@@ -1107,7 +955,11 @@ func (w *worker) generateWork(params *generateParams) (*types.Block, *big.Int, e
// commitWork generates several new sealing tasks based on the parent block
// and submit them to the sealer.
-func (w *worker) commitWork(interrupt *atomic.Int32, noempty bool, timestamp int64) {
+func (w *worker) commitWork(interrupt *atomic.Int32, timestamp int64) {
+ // Abort committing if node is still syncing
+ if w.syncing.Load() {
+ return
+ }
start := time.Now()
// Set the coinbase if the worker is running or it's required
@@ -1126,11 +978,6 @@ func (w *worker) commitWork(interrupt *atomic.Int32, noempty bool, timestamp int
if err != nil {
return
}
- // Create an empty block based on temporary copied state for
- // sealing in advance without waiting block execution finished.
- if !noempty && !w.noempty.Load() {
- w.commit(work.copy(), nil, false, start)
- }
// Fill pending transactions from the txpool into the block.
err = w.fillTransactions(interrupt, work)
switch {
@@ -1184,7 +1031,7 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti
// https://github.com/ethereum/go-ethereum/issues/24299
env := env.copy()
// Withdrawals are set to nil here, because this is only called in PoW.
- block, err := w.engine.FinalizeAndAssemble(w.chain, env.header, env.state, env.txs, env.unclelist(), env.receipts, nil)
+ block, err := w.engine.FinalizeAndAssemble(w.chain, env.header, env.state, env.txs, nil, env.receipts, nil)
if err != nil {
return err
}
@@ -1192,13 +1039,10 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti
if !w.isTTDReached(block.Header()) {
select {
case w.taskCh <- &task{receipts: env.receipts, state: env.state, block: block, createdAt: time.Now()}:
- w.unconfirmed.Shift(block.NumberU64() - 1)
-
fees := totalFees(block, env.receipts)
feesInEther := new(big.Float).Quo(new(big.Float).SetInt(fees), big.NewFloat(params.Ether))
log.Info("Commit new sealing work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()),
- "uncles", len(env.uncles), "txs", env.tcount,
- "gas", block.GasUsed(), "fees", feesInEther,
+ "txs", env.tcount, "gas", block.GasUsed(), "fees", feesInEther,
"elapsed", common.PrettyDuration(time.Since(start)))
case <-w.exitCh:
@@ -1224,7 +1068,6 @@ func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase
coinbase: coinbase,
random: random,
withdrawals: withdrawals,
- noUncle: true,
noTxs: noTxs,
},
result: make(chan *newPayloadResult, 1),
@@ -1258,14 +1101,6 @@ func copyReceipts(receipts []*types.Receipt) []*types.Receipt {
return result
}
-// postSideBlock fires a side chain event, only use it for testing.
-func (w *worker) postSideBlock(event core.ChainSideEvent) {
- select {
- case w.chainSideCh <- event:
- case <-w.exitCh:
- }
-}
-
// totalFees computes total consumed miner fees in Wei. Block transactions and receipts have to have the same order.
func totalFees(block *types.Block, receipts []*types.Receipt) *big.Int {
feesWei := new(big.Int)
diff --git a/miner/worker_test.go b/miner/worker_test.go
index 683d019d2..fb15d365a 100644
--- a/miner/worker_test.go
+++ b/miner/worker_test.go
@@ -17,8 +17,6 @@
package miner
import (
- "crypto/rand"
- "errors"
"math/big"
"sync/atomic"
"testing"
@@ -31,7 +29,6 @@ import (
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/txpool"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
@@ -109,11 +106,10 @@ func init() {
// testWorkerBackend implements worker.Backend interfaces and wraps all information needed during the testing.
type testWorkerBackend struct {
- db ethdb.Database
- txPool *txpool.TxPool
- chain *core.BlockChain
- genesis *core.Genesis
- uncleBlock *types.Block
+ db ethdb.Database
+ txPool *txpool.TxPool
+ chain *core.BlockChain
+ genesis *core.Genesis
}
func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, n int) *testWorkerBackend {
@@ -136,58 +132,16 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine
if err != nil {
t.Fatalf("core.NewBlockChain failed: %v", err)
}
- txpool := txpool.NewTxPool(testTxPoolConfig, chainConfig, chain)
-
- // Generate a small n-block chain and an uncle block for it
- var uncle *types.Block
- if n > 0 {
- genDb, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, n, func(i int, gen *core.BlockGen) {
- gen.SetCoinbase(testBankAddress)
- })
- if _, err := chain.InsertChain(blocks); err != nil {
- t.Fatalf("failed to insert origin chain: %v", err)
- }
- parent := chain.GetBlockByHash(chain.CurrentBlock().ParentHash)
- blocks, _ = core.GenerateChain(chainConfig, parent, engine, genDb, 1, func(i int, gen *core.BlockGen) {
- gen.SetCoinbase(testUserAddress)
- })
- uncle = blocks[0]
- } else {
- _, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, 1, func(i int, gen *core.BlockGen) {
- gen.SetCoinbase(testUserAddress)
- })
- uncle = blocks[0]
- }
return &testWorkerBackend{
- db: db,
- chain: chain,
- txPool: txpool,
- genesis: gspec,
- uncleBlock: uncle,
+ db: db,
+ chain: chain,
+ txPool: txpool.NewTxPool(testTxPoolConfig, chainConfig, chain),
+ genesis: gspec,
}
}
func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain }
func (b *testWorkerBackend) TxPool() *txpool.TxPool { return b.txPool }
-func (b *testWorkerBackend) StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) {
- return nil, errors.New("not supported")
-}
-
-func (b *testWorkerBackend) newRandomUncle() *types.Block {
- var parent *types.Block
- cur := b.chain.CurrentBlock()
- if cur.Number.Uint64() == 0 {
- parent = b.chain.Genesis()
- } else {
- parent = b.chain.GetBlockByHash(b.chain.CurrentBlock().ParentHash)
- }
- blocks, _ := core.GenerateChain(b.chain.Config(), parent, b.chain.Engine(), b.db, 1, func(i int, gen *core.BlockGen) {
- var addr = make([]byte, common.AddressLength)
- rand.Read(addr)
- gen.SetCoinbase(common.BytesToAddress(addr))
- })
- return blocks[0]
-}
func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction {
var tx *types.Transaction
@@ -208,25 +162,15 @@ func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consens
return w, backend
}
-func TestGenerateBlockAndImportClique(t *testing.T) {
- testGenerateBlockAndImport(t, true)
-}
-
-func testGenerateBlockAndImport(t *testing.T, isClique bool) {
+func TestGenerateAndImportBlock(t *testing.T) {
var (
- engine consensus.Engine
- chainConfig params.ChainConfig
- db = rawdb.NewMemoryDatabase()
+ db = rawdb.NewMemoryDatabase()
+ config = *params.AllCliqueProtocolChanges
)
- if isClique {
- chainConfig = *params.AllCliqueProtocolChanges
- chainConfig.Clique = ¶ms.CliqueConfig{Period: 1, Epoch: 30000}
- engine = clique.New(chainConfig.Clique, db)
- } else {
- chainConfig = *params.AllEthashProtocolChanges
- engine = ethash.NewFaker()
- }
- w, b := newTestWorker(t, &chainConfig, engine, db, 0)
+ config.Clique = ¶ms.CliqueConfig{Period: 1, Epoch: 30000}
+ engine := clique.New(config.Clique, db)
+
+ w, b := newTestWorker(t, &config, engine, db, 0)
defer w.close()
// This test chain imports the mined blocks.
@@ -248,8 +192,6 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool) {
for i := 0; i < 5; i++ {
b.txPool.AddLocal(b.newRandomTx(true))
b.txPool.AddLocal(b.newRandomTx(false))
- w.postSideBlock(core.ChainSideEvent{Block: b.newRandomUncle()})
- w.postSideBlock(core.ChainSideEvent{Block: b.newRandomUncle()})
select {
case ev := <-sub.Chan():
@@ -276,17 +218,10 @@ func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consens
w, _ := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0)
defer w.close()
- var (
- taskIndex int
- taskCh = make(chan struct{}, 2)
- )
- checkEqual := func(t *testing.T, task *task, index int) {
- // The first empty work without any txs included
- receiptLen, balance := 0, big.NewInt(0)
- if index == 1 {
- // The second full work with 1 tx included
- receiptLen, balance = 1, big.NewInt(1000)
- }
+ taskCh := make(chan struct{}, 2)
+ checkEqual := func(t *testing.T, task *task) {
+ // The work should contain 1 tx
+ receiptLen, balance := 1, big.NewInt(1000)
if len(task.receipts) != receiptLen {
t.Fatalf("receipt number mismatch: have %d, want %d", len(task.receipts), receiptLen)
}
@@ -296,8 +231,7 @@ func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consens
}
w.newTaskHook = func(task *task) {
if task.block.NumberU64() == 1 {
- checkEqual(t, task, taskIndex)
- taskIndex += 1
+ checkEqual(t, task)
taskCh <- struct{}{}
}
}
@@ -306,122 +240,9 @@ func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consens
time.Sleep(100 * time.Millisecond)
}
w.start() // Start mining!
- for i := 0; i < 2; i += 1 {
- select {
- case <-taskCh:
- case <-time.NewTimer(3 * time.Second).C:
- t.Error("new task timeout")
- }
- }
-}
-
-func TestStreamUncleBlock(t *testing.T) {
- ethash := ethash.NewFaker()
- defer ethash.Close()
-
- w, b := newTestWorker(t, ethashChainConfig, ethash, rawdb.NewMemoryDatabase(), 1)
- defer w.close()
-
- var taskCh = make(chan struct{}, 3)
-
- taskIndex := 0
- w.newTaskHook = func(task *task) {
- if task.block.NumberU64() == 2 {
- // The first task is an empty task, the second
- // one has 1 pending tx, the third one has 1 tx
- // and 1 uncle.
- if taskIndex == 2 {
- have := task.block.Header().UncleHash
- want := types.CalcUncleHash([]*types.Header{b.uncleBlock.Header()})
- if have != want {
- t.Errorf("uncle hash mismatch: have %s, want %s", have.Hex(), want.Hex())
- }
- }
- taskCh <- struct{}{}
- taskIndex += 1
- }
- }
- w.skipSealHook = func(task *task) bool {
- return true
- }
- w.fullTaskHook = func() {
- time.Sleep(100 * time.Millisecond)
- }
- w.start()
-
- for i := 0; i < 2; i += 1 {
- select {
- case <-taskCh:
- case <-time.NewTimer(time.Second).C:
- t.Error("new task timeout")
- }
- }
-
- w.postSideBlock(core.ChainSideEvent{Block: b.uncleBlock})
-
select {
case <-taskCh:
- case <-time.NewTimer(time.Second).C:
- t.Error("new task timeout")
- }
-}
-
-func TestRegenerateMiningBlockEthash(t *testing.T) {
- testRegenerateMiningBlock(t, ethashChainConfig, ethash.NewFaker())
-}
-
-func TestRegenerateMiningBlockClique(t *testing.T) {
- testRegenerateMiningBlock(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase()))
-}
-
-func testRegenerateMiningBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) {
- defer engine.Close()
-
- w, b := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0)
- defer w.close()
-
- var taskCh = make(chan struct{}, 3)
-
- taskIndex := 0
- w.newTaskHook = func(task *task) {
- if task.block.NumberU64() == 1 {
- // The first task is an empty task, the second
- // one has 1 pending tx, the third one has 2 txs
- if taskIndex == 2 {
- receiptLen, balance := 2, big.NewInt(2000)
- if len(task.receipts) != receiptLen {
- t.Errorf("receipt number mismatch: have %d, want %d", len(task.receipts), receiptLen)
- }
- if task.state.GetBalance(testUserAddress).Cmp(balance) != 0 {
- t.Errorf("account balance mismatch: have %d, want %d", task.state.GetBalance(testUserAddress), balance)
- }
- }
- taskCh <- struct{}{}
- taskIndex += 1
- }
- }
- w.skipSealHook = func(task *task) bool {
- return true
- }
- w.fullTaskHook = func() {
- time.Sleep(100 * time.Millisecond)
- }
-
- w.start()
- // Ignore the first two works
- for i := 0; i < 2; i += 1 {
- select {
- case <-taskCh:
- case <-time.NewTimer(time.Second).C:
- t.Error("new task timeout")
- }
- }
- b.txPool.AddLocals(newTxs)
- time.Sleep(time.Second)
-
- select {
- case <-taskCh:
- case <-time.NewTimer(time.Second).C:
+ case <-time.NewTimer(3 * time.Second).C:
t.Error("new task timeout")
}
}
@@ -542,7 +363,6 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
defer w.close()
w.setExtra([]byte{0x01, 0x02})
- w.postSideBlock(core.ChainSideEvent{Block: b.uncleBlock})
w.skipSealHook = func(task *task) bool {
return true
@@ -557,9 +377,6 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
// is even smaller than parent block's. It's OK.
t.Logf("Invalid timestamp, want %d, get %d", timestamp, block.Time())
}
- if len(block.Uncles()) != 0 {
- t.Error("Unexpected uncle block")
- }
_, isClique := engine.(*clique.Clique)
if !isClique {
if len(block.Extra()) != 2 {