Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 29 additions & 9 deletions miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,15 @@ func (w *worker) start() {
for agent := range w.agents {
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() {
Expand Down Expand Up @@ -287,8 +296,12 @@ func (w *worker) update() {

// timeout waiting for v1 initial value
minePeriod := 2
MinePeriodCh := w.engine.(*XDPoS.XDPoS).MinePeriodCh
NewRoundCh := w.engine.(*XDPoS.XDPoS).NewRoundCh
var minePeriodCh <-chan int
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)
defer timeout.Stop()
Expand Down Expand Up @@ -321,7 +334,7 @@ func (w *worker) update() {
for {
// A real event arrived, process interesting content
select {
case v := <-MinePeriodCh:
case v := <-minePeriodCh:
log.Info("[worker] update wait period", "period", v)
minePeriod = v
w.resetCh <- time.Duration(minePeriod) * time.Second
Expand All @@ -340,7 +353,7 @@ func (w *worker) update() {
w.resetCh <- resetTime

// Handle new round
case <-NewRoundCh:
case <-newRoundCh:
w.commitNewWork()
resetTime := getResetTime(w.chain, minePeriod)
w.resetCh <- resetTime
Expand Down Expand Up @@ -664,21 +677,28 @@ func (w *worker) checkPreCommitWithLock() (*types.Block, bool) {
// checkPreCommit checks whether a new work commit is needed,
// returns the parent block and shouldReturn.
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
currentHeader := w.chain.CurrentBlock()
// Guard against nil header (early startup or uninitialised chain).
if currentHeader == nil {
return nil, true
}
if c != nil {
parent = c.FindParentBlockToAssign(w.chain, currentHeader)
if xdposEngine != nil {
parent = xdposEngine.FindParentBlockToAssign(w.chain, currentHeader)
} else {
parent = w.chain.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())
}
if parent == nil {
return nil, true
}
if w.config.XDPoS != nil && xdposEngine == nil {
log.Debug("XDPoS config enabled but consensus engine is not XDPoS")
return parent, true
}
Comment thread
gzliudan marked this conversation as resolved.
if parent.Hash().Hex() == w.lastParentBlockCommit {
return parent, true
}
Expand All @@ -689,8 +709,8 @@ func (w *worker) checkPreCommit() (*types.Block, bool) {
// Only try to commit new work if we are mining
if atomic.LoadInt32(&w.mining) == 1 {
Comment thread
gzliudan marked this conversation as resolved.
// check if we are right after parent's coinbase in the list
if w.config.XDPoS != nil {
ok, err := c.YourTurn(w.chain, parent.Header(), w.coinbase)
if w.config.XDPoS != nil && xdposEngine != nil {
ok, err := xdposEngine.YourTurn(w.chain, parent.Header(), w.coinbase)
if err != nil {
log.Warn("Failed when trying to commit new work", "err", err)
return parent, true
Expand Down
132 changes: 132 additions & 0 deletions miner/worker_test.go
Original file line number Diff line number Diff line change
@@ -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 {
Comment thread
gzliudan marked this conversation as resolved.
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 := &params.ChainConfig{
ChainID: big.NewInt(1),
XDPoS: &params.XDPoSConfig{
V2: &params.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())
}
}