miner: suspend miner if node is syncing (#27218)
Drop the notions of uncles, and disables activities while syncing - Disable activities (e.g. generate pending state) while node is syncing, - Disable empty block submission (but empty block is still kept for payload building), - Drop uncle notion since (ethash is already deprecated)
This commit is contained in:
parent
61dcf76230
commit
d4961881d7
@ -68,6 +68,9 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb
|
|||||||
// Pending block is only known by the miner
|
// Pending block is only known by the miner
|
||||||
if number == rpc.PendingBlockNumber {
|
if number == rpc.PendingBlockNumber {
|
||||||
block := b.eth.miner.PendingBlock()
|
block := b.eth.miner.PendingBlock()
|
||||||
|
if block == nil {
|
||||||
|
return nil, errors.New("pending block is not available")
|
||||||
|
}
|
||||||
return block.Header(), nil
|
return block.Header(), nil
|
||||||
}
|
}
|
||||||
// Otherwise resolve and return the block
|
// 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
|
// Pending block is only known by the miner
|
||||||
if number == rpc.PendingBlockNumber {
|
if number == rpc.PendingBlockNumber {
|
||||||
block := b.eth.miner.PendingBlock()
|
block := b.eth.miner.PendingBlock()
|
||||||
|
if block == nil {
|
||||||
|
return nil, errors.New("pending block is not available")
|
||||||
|
}
|
||||||
return block, nil
|
return block, nil
|
||||||
}
|
}
|
||||||
// Otherwise resolve and return the block
|
// 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
|
// Pending state is only known by the miner
|
||||||
if number == rpc.PendingBlockNumber {
|
if number == rpc.PendingBlockNumber {
|
||||||
block, state := b.eth.miner.Pending()
|
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
|
return state, block.Header(), nil
|
||||||
}
|
}
|
||||||
// Otherwise resolve the block number and return its state
|
// Otherwise resolve the block number and return its state
|
||||||
|
@ -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
|
// both the pending block as well as the pending state from
|
||||||
// the miner and operate on those
|
// the miner and operate on those
|
||||||
_, stateDb := api.eth.miner.Pending()
|
_, stateDb := api.eth.miner.Pending()
|
||||||
|
if stateDb == nil {
|
||||||
|
return state.Dump{}, errors.New("pending state is not available")
|
||||||
|
}
|
||||||
return stateDb.RawDump(opts), nil
|
return stateDb.RawDump(opts), nil
|
||||||
}
|
}
|
||||||
var header *types.Header
|
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
|
// both the pending block as well as the pending state from
|
||||||
// the miner and operate on those
|
// the miner and operate on those
|
||||||
_, stateDb = api.eth.miner.Pending()
|
_, stateDb = api.eth.miner.Pending()
|
||||||
|
if stateDb == nil {
|
||||||
|
return state.IteratorDump{}, errors.New("pending state is not available")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
var header *types.Header
|
var header *types.Header
|
||||||
if number == rpc.LatestBlockNumber {
|
if number == rpc.LatestBlockNumber {
|
||||||
|
@ -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.
|
// pendingLogs returns the logs matching the filter criteria within the pending block.
|
||||||
func (f *Filter) pendingLogs() []*types.Log {
|
func (f *Filter) pendingLogs() []*types.Log {
|
||||||
block, receipts := f.sys.backend.PendingBlockAndReceipts()
|
block, receipts := f.sys.backend.PendingBlockAndReceipts()
|
||||||
if block == nil {
|
if block == nil || receipts == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if bloomFilter(block.Bloom(), f.addresses, f.topics) {
|
if bloomFilter(block.Bloom(), f.addresses, f.topics) {
|
||||||
|
@ -131,16 +131,22 @@ func (miner *Miner) update() {
|
|||||||
shouldStart = true
|
shouldStart = true
|
||||||
log.Info("Mining aborted due to sync")
|
log.Info("Mining aborted due to sync")
|
||||||
}
|
}
|
||||||
|
miner.worker.syncing.Store(true)
|
||||||
|
|
||||||
case downloader.FailedEvent:
|
case downloader.FailedEvent:
|
||||||
canStart = true
|
canStart = true
|
||||||
if shouldStart {
|
if shouldStart {
|
||||||
miner.worker.start()
|
miner.worker.start()
|
||||||
}
|
}
|
||||||
|
miner.worker.syncing.Store(false)
|
||||||
|
|
||||||
case downloader.DoneEvent:
|
case downloader.DoneEvent:
|
||||||
canStart = true
|
canStart = true
|
||||||
if shouldStart {
|
if shouldStart {
|
||||||
miner.worker.start()
|
miner.worker.start()
|
||||||
}
|
}
|
||||||
|
miner.worker.syncing.Store(false)
|
||||||
|
|
||||||
// Stop reacting to downloader events
|
// Stop reacting to downloader events
|
||||||
events.Unsubscribe()
|
events.Unsubscribe()
|
||||||
}
|
}
|
||||||
@ -196,12 +202,14 @@ func (miner *Miner) SetRecommitInterval(interval time.Duration) {
|
|||||||
miner.worker.setRecommitInterval(interval)
|
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) {
|
func (miner *Miner) Pending() (*types.Block, *state.StateDB) {
|
||||||
return miner.worker.pending()
|
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
|
// Note, to access both the pending block and the pending state
|
||||||
// simultaneously, please use Pending(), as the pending state can
|
// 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.
|
// 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) {
|
func (miner *Miner) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
|
||||||
return miner.worker.pendingBlockAndReceipts()
|
return miner.worker.pendingBlockAndReceipts()
|
||||||
}
|
}
|
||||||
@ -225,23 +234,6 @@ func (miner *Miner) SetGasCeil(ceil uint64) {
|
|||||||
miner.worker.setGasCeil(ceil)
|
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
|
// SubscribePendingLogs starts delivering logs from pending transactions
|
||||||
// to the given channel.
|
// to the given channel.
|
||||||
func (miner *Miner) SubscribePendingLogs(ch chan<- []*types.Log) event.Subscription {
|
func (miner *Miner) SubscribePendingLogs(ch chan<- []*types.Log) event.Subscription {
|
||||||
|
@ -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 <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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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 <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)
|
|
||||||
}
|
|
||||||
}
|
|
209
miner/worker.go
209
miner/worker.go
@ -24,7 +24,6 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
mapset "github.com/deckarep/golang-set/v2"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||||
@ -48,15 +47,9 @@ const (
|
|||||||
// chainHeadChanSize is the size of channel listening to ChainHeadEvent.
|
// chainHeadChanSize is the size of channel listening to ChainHeadEvent.
|
||||||
chainHeadChanSize = 10
|
chainHeadChanSize = 10
|
||||||
|
|
||||||
// chainSideChanSize is the size of channel listening to ChainSideEvent.
|
|
||||||
chainSideChanSize = 10
|
|
||||||
|
|
||||||
// resubmitAdjustChanSize is the size of resubmitting interval adjustment channel.
|
// resubmitAdjustChanSize is the size of resubmitting interval adjustment channel.
|
||||||
resubmitAdjustChanSize = 10
|
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
|
// minRecommitInterval is the minimal time interval to recreate the sealing block with
|
||||||
// any newly arrived transactions.
|
// any newly arrived transactions.
|
||||||
minRecommitInterval = 1 * time.Second
|
minRecommitInterval = 1 * time.Second
|
||||||
@ -87,10 +80,7 @@ var (
|
|||||||
// information of the sealing block generation.
|
// information of the sealing block generation.
|
||||||
type environment struct {
|
type environment struct {
|
||||||
signer types.Signer
|
signer types.Signer
|
||||||
|
|
||||||
state *state.StateDB // apply state changes here
|
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
|
tcount int // tx count in cycle
|
||||||
gasPool *core.GasPool // available gas used to pack transactions
|
gasPool *core.GasPool // available gas used to pack transactions
|
||||||
coinbase common.Address
|
coinbase common.Address
|
||||||
@ -98,7 +88,6 @@ type environment struct {
|
|||||||
header *types.Header
|
header *types.Header
|
||||||
txs []*types.Transaction
|
txs []*types.Transaction
|
||||||
receipts []*types.Receipt
|
receipts []*types.Receipt
|
||||||
uncles map[common.Hash]*types.Header
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy creates a deep copy of environment.
|
// copy creates a deep copy of environment.
|
||||||
@ -106,8 +95,6 @@ func (env *environment) copy() *environment {
|
|||||||
cpy := &environment{
|
cpy := &environment{
|
||||||
signer: env.signer,
|
signer: env.signer,
|
||||||
state: env.state.Copy(),
|
state: env.state.Copy(),
|
||||||
ancestors: env.ancestors.Clone(),
|
|
||||||
family: env.family.Clone(),
|
|
||||||
tcount: env.tcount,
|
tcount: env.tcount,
|
||||||
coinbase: env.coinbase,
|
coinbase: env.coinbase,
|
||||||
header: types.CopyHeader(env.header),
|
header: types.CopyHeader(env.header),
|
||||||
@ -117,26 +104,11 @@ func (env *environment) copy() *environment {
|
|||||||
gasPool := *env.gasPool
|
gasPool := *env.gasPool
|
||||||
cpy.gasPool = &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))
|
cpy.txs = make([]*types.Transaction, len(env.txs))
|
||||||
copy(cpy.txs, 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
|
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
|
// discard terminates the background prefetcher go-routine. It should
|
||||||
// always be called for all created environment instances otherwise
|
// always be called for all created environment instances otherwise
|
||||||
// the go-routine leak can happen.
|
// the go-routine leak can happen.
|
||||||
@ -165,7 +137,6 @@ const (
|
|||||||
// newWorkReq represents a request for new sealing work submitting with relative interrupt notifier.
|
// newWorkReq represents a request for new sealing work submitting with relative interrupt notifier.
|
||||||
type newWorkReq struct {
|
type newWorkReq struct {
|
||||||
interrupt *atomic.Int32
|
interrupt *atomic.Int32
|
||||||
noempty bool
|
|
||||||
timestamp int64
|
timestamp int64
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,8 +177,6 @@ type worker struct {
|
|||||||
txsSub event.Subscription
|
txsSub event.Subscription
|
||||||
chainHeadCh chan core.ChainHeadEvent
|
chainHeadCh chan core.ChainHeadEvent
|
||||||
chainHeadSub event.Subscription
|
chainHeadSub event.Subscription
|
||||||
chainSideCh chan core.ChainSideEvent
|
|
||||||
chainSideSub event.Subscription
|
|
||||||
|
|
||||||
// Channels
|
// Channels
|
||||||
newWorkCh chan *newWorkReq
|
newWorkCh chan *newWorkReq
|
||||||
@ -222,9 +191,6 @@ type worker struct {
|
|||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
|
||||||
current *environment // An environment for current running cycle.
|
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.
|
|
||||||
|
|
||||||
mu sync.RWMutex // The lock used to protect the coinbase and extra fields
|
mu sync.RWMutex // The lock used to protect the coinbase and extra fields
|
||||||
coinbase common.Address
|
coinbase common.Address
|
||||||
@ -241,13 +207,7 @@ type worker struct {
|
|||||||
// atomic status counters
|
// atomic status counters
|
||||||
running atomic.Bool // The indicator whether the consensus engine is running or not.
|
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.
|
newTxs atomic.Int32 // New arrival transaction count since last sealing work submitting.
|
||||||
|
syncing atomic.Bool // The indicator whether the node is still syncing.
|
||||||
// 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
|
|
||||||
|
|
||||||
// newpayloadTimeout is the maximum timeout allowance for creating payload.
|
// newpayloadTimeout is the maximum timeout allowance for creating payload.
|
||||||
// The default value is 2 seconds but node operator can set it to arbitrary
|
// 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(),
|
chain: eth.BlockChain(),
|
||||||
mux: mux,
|
mux: mux,
|
||||||
isLocalBlock: isLocalBlock,
|
isLocalBlock: isLocalBlock,
|
||||||
localUncles: make(map[common.Hash]*types.Block),
|
|
||||||
remoteUncles: make(map[common.Hash]*types.Block),
|
|
||||||
unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), sealingLogAtDepth),
|
|
||||||
coinbase: config.Etherbase,
|
coinbase: config.Etherbase,
|
||||||
extra: config.ExtraData,
|
extra: config.ExtraData,
|
||||||
pendingTasks: make(map[common.Hash]*task),
|
pendingTasks: make(map[common.Hash]*task),
|
||||||
txsCh: make(chan core.NewTxsEvent, txChanSize),
|
txsCh: make(chan core.NewTxsEvent, txChanSize),
|
||||||
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
|
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
|
||||||
chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize),
|
|
||||||
newWorkCh: make(chan *newWorkReq),
|
newWorkCh: make(chan *newWorkReq),
|
||||||
getWorkCh: make(chan *getWorkReq),
|
getWorkCh: make(chan *getWorkReq),
|
||||||
taskCh: make(chan *task),
|
taskCh: make(chan *task),
|
||||||
@ -301,7 +257,6 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
|
|||||||
worker.txsSub = eth.TxPool().SubscribeNewTxsEvent(worker.txsCh)
|
worker.txsSub = eth.TxPool().SubscribeNewTxsEvent(worker.txsCh)
|
||||||
// Subscribe events for blockchain
|
// Subscribe events for blockchain
|
||||||
worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh)
|
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.
|
// Sanitize recommit interval if the user-specified one is too short.
|
||||||
recommit := worker.config.Recommit
|
recommit := worker.config.Recommit
|
||||||
@ -370,19 +325,9 @@ func (w *worker) setRecommitInterval(interval time.Duration) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// disablePreseal disables pre-sealing feature
|
// pending returns the pending state and corresponding block. The returned
|
||||||
func (w *worker) disablePreseal() {
|
// values can be nil in case the pending block is not initialized.
|
||||||
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.
|
|
||||||
func (w *worker) pending() (*types.Block, *state.StateDB) {
|
func (w *worker) pending() (*types.Block, *state.StateDB) {
|
||||||
// return a snapshot to avoid contention on currentMu mutex
|
|
||||||
w.snapshotMu.RLock()
|
w.snapshotMu.RLock()
|
||||||
defer w.snapshotMu.RUnlock()
|
defer w.snapshotMu.RUnlock()
|
||||||
if w.snapshotState == nil {
|
if w.snapshotState == nil {
|
||||||
@ -391,17 +336,17 @@ func (w *worker) pending() (*types.Block, *state.StateDB) {
|
|||||||
return w.snapshotBlock, w.snapshotState.Copy()
|
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 {
|
func (w *worker) pendingBlock() *types.Block {
|
||||||
// return a snapshot to avoid contention on currentMu mutex
|
|
||||||
w.snapshotMu.RLock()
|
w.snapshotMu.RLock()
|
||||||
defer w.snapshotMu.RUnlock()
|
defer w.snapshotMu.RUnlock()
|
||||||
return w.snapshotBlock
|
return w.snapshotBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
// pendingBlockAndReceipts returns pending block and corresponding receipts.
|
// 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) {
|
func (w *worker) pendingBlockAndReceipts() (*types.Block, types.Receipts) {
|
||||||
// return a snapshot to avoid contention on currentMu mutex
|
|
||||||
w.snapshotMu.RLock()
|
w.snapshotMu.RLock()
|
||||||
defer w.snapshotMu.RUnlock()
|
defer w.snapshotMu.RUnlock()
|
||||||
return w.snapshotBlock, w.snapshotReceipts
|
return w.snapshotBlock, w.snapshotReceipts
|
||||||
@ -467,13 +412,13 @@ func (w *worker) newWorkLoop(recommit time.Duration) {
|
|||||||
<-timer.C // discard the initial tick
|
<-timer.C // discard the initial tick
|
||||||
|
|
||||||
// commit aborts in-flight transaction execution with given signal and resubmits a new one.
|
// 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 {
|
if interrupt != nil {
|
||||||
interrupt.Store(s)
|
interrupt.Store(s)
|
||||||
}
|
}
|
||||||
interrupt = new(atomic.Int32)
|
interrupt = new(atomic.Int32)
|
||||||
select {
|
select {
|
||||||
case w.newWorkCh <- &newWorkReq{interrupt: interrupt, noempty: noempty, timestamp: timestamp}:
|
case w.newWorkCh <- &newWorkReq{interrupt: interrupt, timestamp: timestamp}:
|
||||||
case <-w.exitCh:
|
case <-w.exitCh:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -496,12 +441,12 @@ func (w *worker) newWorkLoop(recommit time.Duration) {
|
|||||||
case <-w.startCh:
|
case <-w.startCh:
|
||||||
clearPending(w.chain.CurrentBlock().Number.Uint64())
|
clearPending(w.chain.CurrentBlock().Number.Uint64())
|
||||||
timestamp = time.Now().Unix()
|
timestamp = time.Now().Unix()
|
||||||
commit(false, commitInterruptNewHead)
|
commit(commitInterruptNewHead)
|
||||||
|
|
||||||
case head := <-w.chainHeadCh:
|
case head := <-w.chainHeadCh:
|
||||||
clearPending(head.Block.NumberU64())
|
clearPending(head.Block.NumberU64())
|
||||||
timestamp = time.Now().Unix()
|
timestamp = time.Now().Unix()
|
||||||
commit(false, commitInterruptNewHead)
|
commit(commitInterruptNewHead)
|
||||||
|
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
// If sealing is running resubmit a new work cycle periodically to pull in
|
// 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)
|
timer.Reset(recommit)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
commit(true, commitInterruptResubmit)
|
commit(commitInterruptResubmit)
|
||||||
}
|
}
|
||||||
|
|
||||||
case interval := <-w.resubmitIntervalCh:
|
case interval := <-w.resubmitIntervalCh:
|
||||||
@ -558,20 +503,16 @@ func (w *worker) mainLoop() {
|
|||||||
defer w.wg.Done()
|
defer w.wg.Done()
|
||||||
defer w.txsSub.Unsubscribe()
|
defer w.txsSub.Unsubscribe()
|
||||||
defer w.chainHeadSub.Unsubscribe()
|
defer w.chainHeadSub.Unsubscribe()
|
||||||
defer w.chainSideSub.Unsubscribe()
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if w.current != nil {
|
if w.current != nil {
|
||||||
w.current.discard()
|
w.current.discard()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
cleanTicker := time.NewTicker(time.Second * 10)
|
|
||||||
defer cleanTicker.Stop()
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case req := <-w.newWorkCh:
|
case req := <-w.newWorkCh:
|
||||||
w.commitWork(req.interrupt, req.noempty, req.timestamp)
|
w.commitWork(req.interrupt, req.timestamp)
|
||||||
|
|
||||||
case req := <-w.getWorkCh:
|
case req := <-w.getWorkCh:
|
||||||
block, fees, err := w.generateWork(req.params)
|
block, fees, err := w.generateWork(req.params)
|
||||||
@ -580,42 +521,6 @@ func (w *worker) mainLoop() {
|
|||||||
block: block,
|
block: block,
|
||||||
fees: fees,
|
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:
|
case ev := <-w.txsCh:
|
||||||
// Apply transactions to the pending state if we're not sealing
|
// 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
|
// submit sealing work here since all empty submission will be rejected
|
||||||
// by clique. Of course the advance sealing(empty submission) is disabled.
|
// by clique. Of course the advance sealing(empty submission) is disabled.
|
||||||
if w.chainConfig.Clique != nil && w.chainConfig.Clique.Period == 0 {
|
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)))
|
w.newTxs.Add(int32(len(ev.Txs)))
|
||||||
@ -659,8 +564,6 @@ func (w *worker) mainLoop() {
|
|||||||
return
|
return
|
||||||
case <-w.chainHeadSub.Err():
|
case <-w.chainHeadSub.Err():
|
||||||
return
|
return
|
||||||
case <-w.chainSideSub.Err():
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -780,9 +683,6 @@ func (w *worker) resultLoop() {
|
|||||||
// Broadcast the block and announce chain insertion event
|
// Broadcast the block and announce chain insertion event
|
||||||
w.mux.Post(core.NewMinedBlockEvent{Block: block})
|
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:
|
case <-w.exitCh:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -804,46 +704,13 @@ func (w *worker) makeEnv(parent *types.Header, header *types.Header, coinbase co
|
|||||||
signer: types.MakeSigner(w.chainConfig, header.Number, header.Time),
|
signer: types.MakeSigner(w.chainConfig, header.Number, header.Time),
|
||||||
state: state,
|
state: state,
|
||||||
coinbase: coinbase,
|
coinbase: coinbase,
|
||||||
ancestors: mapset.NewSet[common.Hash](),
|
|
||||||
family: mapset.NewSet[common.Hash](),
|
|
||||||
header: header,
|
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())
|
|
||||||
}
|
}
|
||||||
// Keep track of transactions which return errors so they can be removed
|
// Keep track of transactions which return errors so they can be removed
|
||||||
env.tcount = 0
|
env.tcount = 0
|
||||||
return env, nil
|
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.
|
// updateSnapshot updates pending snapshot block, receipts and state.
|
||||||
func (w *worker) updateSnapshot(env *environment) {
|
func (w *worker) updateSnapshot(env *environment) {
|
||||||
w.snapshotMu.Lock()
|
w.snapshotMu.Lock()
|
||||||
@ -852,7 +719,7 @@ func (w *worker) updateSnapshot(env *environment) {
|
|||||||
w.snapshotBlock = types.NewBlock(
|
w.snapshotBlock = types.NewBlock(
|
||||||
env.header,
|
env.header,
|
||||||
env.txs,
|
env.txs,
|
||||||
env.unclelist(),
|
nil,
|
||||||
env.receipts,
|
env.receipts,
|
||||||
trie.NewStackTrie(nil),
|
trie.NewStackTrie(nil),
|
||||||
)
|
)
|
||||||
@ -962,7 +829,6 @@ type generateParams struct {
|
|||||||
coinbase common.Address // The fee recipient address for including transaction
|
coinbase common.Address // The fee recipient address for including transaction
|
||||||
random common.Hash // The randomness generated by beacon chain, empty before the merge
|
random common.Hash // The randomness generated by beacon chain, empty before the merge
|
||||||
withdrawals types.Withdrawals // List of withdrawals to include in block.
|
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
|
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)
|
log.Error("Failed to create sealing context", "err", err)
|
||||||
return nil, 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
|
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))
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
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
|
// commitWork generates several new sealing tasks based on the parent block
|
||||||
// and submit them to the sealer.
|
// 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()
|
start := time.Now()
|
||||||
|
|
||||||
// Set the coinbase if the worker is running or it's required
|
// 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 {
|
if err != nil {
|
||||||
return
|
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.
|
// Fill pending transactions from the txpool into the block.
|
||||||
err = w.fillTransactions(interrupt, work)
|
err = w.fillTransactions(interrupt, work)
|
||||||
switch {
|
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
|
// https://github.com/ethereum/go-ethereum/issues/24299
|
||||||
env := env.copy()
|
env := env.copy()
|
||||||
// Withdrawals are set to nil here, because this is only called in PoW.
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1192,13 +1039,10 @@ func (w *worker) commit(env *environment, interval func(), update bool, start ti
|
|||||||
if !w.isTTDReached(block.Header()) {
|
if !w.isTTDReached(block.Header()) {
|
||||||
select {
|
select {
|
||||||
case w.taskCh <- &task{receipts: env.receipts, state: env.state, block: block, createdAt: time.Now()}:
|
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)
|
fees := totalFees(block, env.receipts)
|
||||||
feesInEther := new(big.Float).Quo(new(big.Float).SetInt(fees), big.NewFloat(params.Ether))
|
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()),
|
log.Info("Commit new sealing work", "number", block.Number(), "sealhash", w.engine.SealHash(block.Header()),
|
||||||
"uncles", len(env.uncles), "txs", env.tcount,
|
"txs", env.tcount, "gas", block.GasUsed(), "fees", feesInEther,
|
||||||
"gas", block.GasUsed(), "fees", feesInEther,
|
|
||||||
"elapsed", common.PrettyDuration(time.Since(start)))
|
"elapsed", common.PrettyDuration(time.Since(start)))
|
||||||
|
|
||||||
case <-w.exitCh:
|
case <-w.exitCh:
|
||||||
@ -1224,7 +1068,6 @@ func (w *worker) getSealingBlock(parent common.Hash, timestamp uint64, coinbase
|
|||||||
coinbase: coinbase,
|
coinbase: coinbase,
|
||||||
random: random,
|
random: random,
|
||||||
withdrawals: withdrawals,
|
withdrawals: withdrawals,
|
||||||
noUncle: true,
|
|
||||||
noTxs: noTxs,
|
noTxs: noTxs,
|
||||||
},
|
},
|
||||||
result: make(chan *newPayloadResult, 1),
|
result: make(chan *newPayloadResult, 1),
|
||||||
@ -1258,14 +1101,6 @@ func copyReceipts(receipts []*types.Receipt) []*types.Receipt {
|
|||||||
return result
|
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.
|
// 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 {
|
func totalFees(block *types.Block, receipts []*types.Receipt) *big.Int {
|
||||||
feesWei := new(big.Int)
|
feesWei := new(big.Int)
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
package miner
|
package miner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"errors"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
@ -31,7 +29,6 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"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/txpool"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
@ -113,7 +110,6 @@ type testWorkerBackend struct {
|
|||||||
txPool *txpool.TxPool
|
txPool *txpool.TxPool
|
||||||
chain *core.BlockChain
|
chain *core.BlockChain
|
||||||
genesis *core.Genesis
|
genesis *core.Genesis
|
||||||
uncleBlock *types.Block
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, n int) *testWorkerBackend {
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("core.NewBlockChain failed: %v", err)
|
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{
|
return &testWorkerBackend{
|
||||||
db: db,
|
db: db,
|
||||||
chain: chain,
|
chain: chain,
|
||||||
txPool: txpool,
|
txPool: txpool.NewTxPool(testTxPoolConfig, chainConfig, chain),
|
||||||
genesis: gspec,
|
genesis: gspec,
|
||||||
uncleBlock: uncle,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain }
|
func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain }
|
||||||
func (b *testWorkerBackend) TxPool() *txpool.TxPool { return b.txPool }
|
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 {
|
func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction {
|
||||||
var tx *types.Transaction
|
var tx *types.Transaction
|
||||||
@ -208,25 +162,15 @@ func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consens
|
|||||||
return w, backend
|
return w, backend
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateBlockAndImportClique(t *testing.T) {
|
func TestGenerateAndImportBlock(t *testing.T) {
|
||||||
testGenerateBlockAndImport(t, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testGenerateBlockAndImport(t *testing.T, isClique bool) {
|
|
||||||
var (
|
var (
|
||||||
engine consensus.Engine
|
|
||||||
chainConfig params.ChainConfig
|
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
|
config = *params.AllCliqueProtocolChanges
|
||||||
)
|
)
|
||||||
if isClique {
|
config.Clique = ¶ms.CliqueConfig{Period: 1, Epoch: 30000}
|
||||||
chainConfig = *params.AllCliqueProtocolChanges
|
engine := clique.New(config.Clique, db)
|
||||||
chainConfig.Clique = ¶ms.CliqueConfig{Period: 1, Epoch: 30000}
|
|
||||||
engine = clique.New(chainConfig.Clique, db)
|
w, b := newTestWorker(t, &config, engine, db, 0)
|
||||||
} else {
|
|
||||||
chainConfig = *params.AllEthashProtocolChanges
|
|
||||||
engine = ethash.NewFaker()
|
|
||||||
}
|
|
||||||
w, b := newTestWorker(t, &chainConfig, engine, db, 0)
|
|
||||||
defer w.close()
|
defer w.close()
|
||||||
|
|
||||||
// This test chain imports the mined blocks.
|
// This test chain imports the mined blocks.
|
||||||
@ -248,8 +192,6 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool) {
|
|||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
b.txPool.AddLocal(b.newRandomTx(true))
|
b.txPool.AddLocal(b.newRandomTx(true))
|
||||||
b.txPool.AddLocal(b.newRandomTx(false))
|
b.txPool.AddLocal(b.newRandomTx(false))
|
||||||
w.postSideBlock(core.ChainSideEvent{Block: b.newRandomUncle()})
|
|
||||||
w.postSideBlock(core.ChainSideEvent{Block: b.newRandomUncle()})
|
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case ev := <-sub.Chan():
|
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)
|
w, _ := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0)
|
||||||
defer w.close()
|
defer w.close()
|
||||||
|
|
||||||
var (
|
taskCh := make(chan struct{}, 2)
|
||||||
taskIndex int
|
checkEqual := func(t *testing.T, task *task) {
|
||||||
taskCh = make(chan struct{}, 2)
|
// The work should contain 1 tx
|
||||||
)
|
receiptLen, balance := 1, big.NewInt(1000)
|
||||||
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)
|
|
||||||
}
|
|
||||||
if len(task.receipts) != receiptLen {
|
if len(task.receipts) != receiptLen {
|
||||||
t.Fatalf("receipt number mismatch: have %d, want %d", 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) {
|
w.newTaskHook = func(task *task) {
|
||||||
if task.block.NumberU64() == 1 {
|
if task.block.NumberU64() == 1 {
|
||||||
checkEqual(t, task, taskIndex)
|
checkEqual(t, task)
|
||||||
taskIndex += 1
|
|
||||||
taskCh <- struct{}{}
|
taskCh <- struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -306,124 +240,11 @@ func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consens
|
|||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
}
|
}
|
||||||
w.start() // Start mining!
|
w.start() // Start mining!
|
||||||
for i := 0; i < 2; i += 1 {
|
|
||||||
select {
|
select {
|
||||||
case <-taskCh:
|
case <-taskCh:
|
||||||
case <-time.NewTimer(3 * time.Second).C:
|
case <-time.NewTimer(3 * time.Second).C:
|
||||||
t.Error("new task timeout")
|
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:
|
|
||||||
t.Error("new task timeout")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAdjustIntervalEthash(t *testing.T) {
|
func TestAdjustIntervalEthash(t *testing.T) {
|
||||||
@ -542,7 +363,6 @@ func testGetSealingWork(t *testing.T, chainConfig *params.ChainConfig, engine co
|
|||||||
defer w.close()
|
defer w.close()
|
||||||
|
|
||||||
w.setExtra([]byte{0x01, 0x02})
|
w.setExtra([]byte{0x01, 0x02})
|
||||||
w.postSideBlock(core.ChainSideEvent{Block: b.uncleBlock})
|
|
||||||
|
|
||||||
w.skipSealHook = func(task *task) bool {
|
w.skipSealHook = func(task *task) bool {
|
||||||
return true
|
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.
|
// is even smaller than parent block's. It's OK.
|
||||||
t.Logf("Invalid timestamp, want %d, get %d", timestamp, block.Time())
|
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)
|
_, isClique := engine.(*clique.Clique)
|
||||||
if !isClique {
|
if !isClique {
|
||||||
if len(block.Extra()) != 2 {
|
if len(block.Extra()) != 2 {
|
||||||
|
Loading…
Reference in New Issue
Block a user