mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-18 02:40:45 +00:00
fix(miner): avoid XDPoS-only paths on non-XDPoS engines (#2049)
This commit is contained in:
parent
d542efe08d
commit
0b47621d05
2 changed files with 161 additions and 9 deletions
|
|
@ -248,6 +248,15 @@ func (w *worker) start() {
|
||||||
for agent := range w.agents {
|
for agent := range w.agents {
|
||||||
agent.Start()
|
agent.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify config and engine
|
||||||
|
var xdposEngine *XDPoS.XDPoS
|
||||||
|
if engine, ok := w.engine.(*XDPoS.XDPoS); ok {
|
||||||
|
xdposEngine = engine
|
||||||
|
}
|
||||||
|
if w.config != nil && w.config.XDPoS != nil && xdposEngine == nil {
|
||||||
|
log.Warn("XDPoS config enabled but consensus engine is not XDPoS")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *worker) stop() {
|
func (w *worker) stop() {
|
||||||
|
|
@ -287,8 +296,12 @@ func (w *worker) update() {
|
||||||
|
|
||||||
// timeout waiting for v1 initial value
|
// timeout waiting for v1 initial value
|
||||||
minePeriod := 2
|
minePeriod := 2
|
||||||
MinePeriodCh := w.engine.(*XDPoS.XDPoS).MinePeriodCh
|
var minePeriodCh <-chan int
|
||||||
NewRoundCh := w.engine.(*XDPoS.XDPoS).NewRoundCh
|
var newRoundCh <-chan types.Round
|
||||||
|
if xdposEngine, ok := w.engine.(*XDPoS.XDPoS); ok {
|
||||||
|
minePeriodCh = xdposEngine.MinePeriodCh
|
||||||
|
newRoundCh = xdposEngine.NewRoundCh
|
||||||
|
}
|
||||||
|
|
||||||
timeout := time.NewTimer(time.Duration(minePeriod) * time.Second)
|
timeout := time.NewTimer(time.Duration(minePeriod) * time.Second)
|
||||||
defer timeout.Stop()
|
defer timeout.Stop()
|
||||||
|
|
@ -321,7 +334,7 @@ func (w *worker) update() {
|
||||||
for {
|
for {
|
||||||
// A real event arrived, process interesting content
|
// A real event arrived, process interesting content
|
||||||
select {
|
select {
|
||||||
case v := <-MinePeriodCh:
|
case v := <-minePeriodCh:
|
||||||
log.Info("[worker] update wait period", "period", v)
|
log.Info("[worker] update wait period", "period", v)
|
||||||
minePeriod = v
|
minePeriod = v
|
||||||
w.resetCh <- time.Duration(minePeriod) * time.Second
|
w.resetCh <- time.Duration(minePeriod) * time.Second
|
||||||
|
|
@ -340,7 +353,7 @@ func (w *worker) update() {
|
||||||
w.resetCh <- resetTime
|
w.resetCh <- resetTime
|
||||||
|
|
||||||
// Handle new round
|
// Handle new round
|
||||||
case <-NewRoundCh:
|
case <-newRoundCh:
|
||||||
w.commitNewWork()
|
w.commitNewWork()
|
||||||
resetTime := getResetTime(w.chain, minePeriod)
|
resetTime := getResetTime(w.chain, minePeriod)
|
||||||
w.resetCh <- resetTime
|
w.resetCh <- resetTime
|
||||||
|
|
@ -664,21 +677,28 @@ func (w *worker) checkPreCommitWithLock() (*types.Block, bool) {
|
||||||
// checkPreCommit checks whether a new work commit is needed,
|
// checkPreCommit checks whether a new work commit is needed,
|
||||||
// returns the parent block and shouldReturn.
|
// returns the parent block and shouldReturn.
|
||||||
func (w *worker) checkPreCommit() (*types.Block, bool) {
|
func (w *worker) checkPreCommit() (*types.Block, bool) {
|
||||||
c := w.engine.(*XDPoS.XDPoS)
|
var xdposEngine *XDPoS.XDPoS
|
||||||
|
if engine, ok := w.engine.(*XDPoS.XDPoS); ok {
|
||||||
|
xdposEngine = engine
|
||||||
|
}
|
||||||
var parent *types.Block
|
var parent *types.Block
|
||||||
currentHeader := w.chain.CurrentBlock()
|
currentHeader := w.chain.CurrentBlock()
|
||||||
// Guard against nil header (early startup or uninitialised chain).
|
// Guard against nil header (early startup or uninitialised chain).
|
||||||
if currentHeader == nil {
|
if currentHeader == nil {
|
||||||
return nil, true
|
return nil, true
|
||||||
}
|
}
|
||||||
if c != nil {
|
if xdposEngine != nil {
|
||||||
parent = c.FindParentBlockToAssign(w.chain, currentHeader)
|
parent = xdposEngine.FindParentBlockToAssign(w.chain, currentHeader)
|
||||||
} else {
|
} else {
|
||||||
parent = w.chain.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())
|
parent = w.chain.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())
|
||||||
}
|
}
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
return nil, true
|
return nil, true
|
||||||
}
|
}
|
||||||
|
if w.config.XDPoS != nil && xdposEngine == nil {
|
||||||
|
log.Debug("XDPoS config enabled but consensus engine is not XDPoS")
|
||||||
|
return parent, true
|
||||||
|
}
|
||||||
if parent.Hash().Hex() == w.lastParentBlockCommit {
|
if parent.Hash().Hex() == w.lastParentBlockCommit {
|
||||||
return parent, true
|
return parent, true
|
||||||
}
|
}
|
||||||
|
|
@ -689,8 +709,8 @@ func (w *worker) checkPreCommit() (*types.Block, bool) {
|
||||||
// Only try to commit new work if we are mining
|
// Only try to commit new work if we are mining
|
||||||
if atomic.LoadInt32(&w.mining) == 1 {
|
if atomic.LoadInt32(&w.mining) == 1 {
|
||||||
// check if we are right after parent's coinbase in the list
|
// check if we are right after parent's coinbase in the list
|
||||||
if w.config.XDPoS != nil {
|
if w.config.XDPoS != nil && xdposEngine != nil {
|
||||||
ok, err := c.YourTurn(w.chain, parent.Header(), w.coinbase)
|
ok, err := xdposEngine.YourTurn(w.chain, parent.Header(), w.coinbase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Failed when trying to commit new work", "err", err)
|
log.Warn("Failed when trying to commit new work", "err", err)
|
||||||
return parent, true
|
return parent, true
|
||||||
|
|
|
||||||
132
miner/worker_test.go
Normal file
132
miner/worker_test.go
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
// Copyright 2026 The XDPoSChain Authors
|
||||||
|
// This file is part of the XDPoSChain library.
|
||||||
|
//
|
||||||
|
// The XDPoSChain 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 XDPoSChain 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 XDPoSChain library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package miner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/vm"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/event"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newBlockingSubscription() event.Subscription {
|
||||||
|
return event.NewSubscription(func(unsub <-chan struct{}) error {
|
||||||
|
<-unsub
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWorkerUpdateNonXDPoSStaysRunning(t *testing.T) {
|
||||||
|
worker := &worker{
|
||||||
|
engine: ethash.NewFaker(),
|
||||||
|
chainHeadSub: newBlockingSubscription(),
|
||||||
|
chainSideSub: newBlockingSubscription(),
|
||||||
|
resetCh: make(chan time.Duration, 1),
|
||||||
|
}
|
||||||
|
|
||||||
|
done := make(chan struct{})
|
||||||
|
started := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
close(started)
|
||||||
|
worker.update()
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-started:
|
||||||
|
// worker.update has started; proceed with timing checks.
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
t.Fatal("worker.update did not start in time")
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
t.Fatal("worker.update returned before unsubscribe")
|
||||||
|
default:
|
||||||
|
// Expected: update is still running until subscription error.
|
||||||
|
}
|
||||||
|
worker.chainHeadSub.Unsubscribe()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
// Expected: update exits after subscription error.
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
t.Fatal("worker.update did not return after unsubscribe")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWorkerCheckPreCommitXDPoSMismatch(t *testing.T) {
|
||||||
|
config := ¶ms.ChainConfig{
|
||||||
|
ChainID: big.NewInt(1),
|
||||||
|
XDPoS: ¶ms.XDPoSConfig{
|
||||||
|
V2: ¶ms.V2{
|
||||||
|
SwitchBlock: big.NewInt(0),
|
||||||
|
AllConfigs: map[uint64]*params.V2Config{
|
||||||
|
0: {MinePeriod: 2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
signer := common.HexToAddress("0x0000000000000000000000000000000000000001")
|
||||||
|
extraData := make([]byte, 0, utils.ExtraVanity+common.AddressLength+utils.ExtraSeal)
|
||||||
|
extraData = append(extraData, make([]byte, utils.ExtraVanity)...)
|
||||||
|
extraData = append(extraData, signer.Bytes()...)
|
||||||
|
extraData = append(extraData, make([]byte, utils.ExtraSeal)...)
|
||||||
|
genesis := &core.Genesis{
|
||||||
|
Config: config,
|
||||||
|
GasLimit: params.TargetGasLimit,
|
||||||
|
Difficulty: big.NewInt(1),
|
||||||
|
Alloc: types.GenesisAlloc{},
|
||||||
|
ExtraData: extraData,
|
||||||
|
}
|
||||||
|
db := rawdb.NewMemoryDatabase()
|
||||||
|
if _, err := genesis.Commit(db); err != nil {
|
||||||
|
t.Fatalf("failed to commit genesis: %v", err)
|
||||||
|
}
|
||||||
|
engine := ethash.NewFaker()
|
||||||
|
chain, err := core.NewBlockChain(db, nil, genesis, engine, vm.Config{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create blockchain: %v", err)
|
||||||
|
}
|
||||||
|
defer chain.Stop()
|
||||||
|
|
||||||
|
worker := &worker{
|
||||||
|
config: config,
|
||||||
|
engine: engine,
|
||||||
|
chain: chain,
|
||||||
|
announceTxs: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
parent, shouldReturn := worker.checkPreCommitWithLock()
|
||||||
|
if parent == nil {
|
||||||
|
t.Fatal("expected parent block, got nil")
|
||||||
|
}
|
||||||
|
if !shouldReturn {
|
||||||
|
t.Fatal("expected checkPreCommitWithLock to skip when XDPoS config is enabled but engine is not XDPoS")
|
||||||
|
}
|
||||||
|
if parent.Number().Sign() != 0 {
|
||||||
|
t.Fatalf("expected genesis parent, got number %v", parent.Number())
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue