mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 13:21:37 +00:00
fix(consensus): fix private chain initialization (#1987)
* revert: use masternodes from snapshot to verify vote * fix underflow during chain initialization * add previously removed test * rename snapshot > snap for consistency
This commit is contained in:
parent
296d612167
commit
e324a78d94
4 changed files with 34 additions and 73 deletions
|
|
@ -129,7 +129,7 @@ func (x *XDPoS_v2) getTCEpochInfo(chain consensus.ChainReader, timeoutRound type
|
|||
Number: epochSwitchInfo.EpochSwitchBlockInfo.Number,
|
||||
}
|
||||
log.Info("[getTCEpochInfo] Init epochInfo", "number", epochBlockInfo.Number, "round", epochRound, "tcRound", timeoutRound, "tcEpoch", tempTCEpoch)
|
||||
for epochBlockInfo.Round > timeoutRound {
|
||||
for epochBlockInfo.Round > timeoutRound && tempTCEpoch > 0 {
|
||||
tempTCEpoch--
|
||||
epochBlockInfo, err = x.GetBlockByEpochNumber(chain, tempTCEpoch)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -22,29 +22,22 @@ func (x *XDPoS_v2) VerifyVoteMessage(chain consensus.ChainReader, vote *types.Vo
|
|||
return false, nil
|
||||
}
|
||||
|
||||
// If we don't yet have the referenced header locally, defer verification.
|
||||
// Votes may arrive before the block header itself; avoid logging an error
|
||||
// for that normal timing condition and let the vote be retried later.
|
||||
if chain.GetHeaderByHash(vote.ProposedBlockInfo.Hash) == nil {
|
||||
log.Debug("[VerifyVoteMessage] referenced header not present yet, defer verification", "blockNum", vote.ProposedBlockInfo.Number, "blockHash", vote.ProposedBlockInfo.Hash)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
epochInfo, err := x.getEpochSwitchInfo(chain, nil, vote.ProposedBlockInfo.Hash)
|
||||
snap, err := x.getSnapshot(chain, vote.GapNumber, true)
|
||||
if err != nil {
|
||||
log.Error("[VerifyVoteMessage] Fail to get epochInfo when verifying vote message!", "blockNum", vote.ProposedBlockInfo.Number, "blockHash", vote.ProposedBlockInfo.Hash, "voteHash", vote.Hash(), "voteGapNumber", vote.GapNumber, "err", err)
|
||||
log.Error("[VerifyVoteMessage] fail to get snapshot for a vote message", "blockNum", vote.ProposedBlockInfo.Number, "blockHash", vote.ProposedBlockInfo.Hash, "voteHash", vote.Hash(), "err", err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
verified, signer, err := x.verifyMsgSignature(types.VoteSigHash(&types.VoteForSign{
|
||||
ProposedBlockInfo: vote.ProposedBlockInfo,
|
||||
GapNumber: vote.GapNumber,
|
||||
}), vote.Signature, epochInfo.Masternodes)
|
||||
}), vote.Signature, snap.NextEpochCandidates)
|
||||
|
||||
if err != nil {
|
||||
for i, mn := range epochInfo.Masternodes {
|
||||
for i, mn := range snap.NextEpochCandidates {
|
||||
log.Warn("[VerifyVoteMessage] Master node list item", "index", i, "Master node", mn.Hex())
|
||||
}
|
||||
log.Warn("[VerifyVoteMessage] Error while verifying vote message", "votedBlockNum", vote.ProposedBlockInfo.Number.Uint64(), "votedBlockHash", vote.ProposedBlockInfo.Hash.Hex(), "voteHash", vote.Hash(), "error", err.Error())
|
||||
log.Warn("[VerifyVoteMessage] Error while verifying vote message", "votedBlockNum", vote.ProposedBlockInfo.Number.Uint64(), "votedBlockHash", vote.ProposedBlockInfo.Hash.Hex(), "voteHash", vote.Hash(), "err", err)
|
||||
return false, err
|
||||
}
|
||||
vote.SetSigner(signer)
|
||||
|
|
@ -198,7 +191,7 @@ func (x *XDPoS_v2) verifyVotes(chain consensus.ChainReader, votes map[common.Has
|
|||
})
|
||||
verified, masterNode, err := x.verifyMsgSignature(signedVote, v.Signature, masternodes)
|
||||
if err != nil {
|
||||
log.Warn("[verifyVotes] error while verifying vote signature", "error", err.Error())
|
||||
log.Warn("[verifyVotes] error while verifying vote signature", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import (
|
|||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
|
@ -96,63 +95,6 @@ func (m *MockChainReader) GetBlock(hash common.Hash, number uint64) *types.Block
|
|||
return nil
|
||||
}
|
||||
|
||||
// TestVerifyVoteMessage_HeaderNotPresent tests the behavior when a vote arrives
|
||||
// before its corresponding block header is available.
|
||||
//
|
||||
// This test verifies that VerifyVoteMessage handles the normal timing condition
|
||||
// where votes may arrive before the block header itself, returning (false, nil)
|
||||
// and logging at Debug level rather than Error level.
|
||||
func TestVerifyVoteMessage_HeaderNotPresent(t *testing.T) {
|
||||
// Create a mock chain reader with no headers
|
||||
mockChain := NewMockChainReader()
|
||||
|
||||
// Capture logs to ensure Debug (and no Error) is emitted
|
||||
memHandler := newMemoryHandler()
|
||||
log.SetDefault(log.NewLogger(memHandler))
|
||||
defer log.SetDefault(log.NewLogger(log.DiscardHandler()))
|
||||
|
||||
// Create the XDPoS_v2 engine
|
||||
engine := &XDPoS_v2{
|
||||
currentRound: 10,
|
||||
lock: sync.RWMutex{},
|
||||
}
|
||||
|
||||
// Create a vote for a block that doesn't exist in the chain
|
||||
vote := &types.Vote{
|
||||
ProposedBlockInfo: &types.BlockInfo{
|
||||
Hash: common.StringToHash("nonexistent-block"),
|
||||
Round: 10,
|
||||
Number: big.NewInt(100),
|
||||
},
|
||||
Signature: make([]byte, 65),
|
||||
GapNumber: 0,
|
||||
}
|
||||
|
||||
// Call VerifyVoteMessage
|
||||
verified, err := engine.VerifyVoteMessage(mockChain, vote)
|
||||
|
||||
// Verify the expected behavior:
|
||||
// 1. Should return false (not verified)
|
||||
assert.False(t, verified, "Should return false when header is not present")
|
||||
|
||||
// 2. Should return nil error (deferred verification)
|
||||
assert.NoError(t, err, "Should not error when header is absent; vote will be retried")
|
||||
|
||||
// 3. Should log at Debug level and not emit Error-level logs
|
||||
records := memHandler.Records()
|
||||
var hasDebug, hasError bool
|
||||
for _, rec := range records {
|
||||
switch rec.Level {
|
||||
case slog.LevelDebug:
|
||||
hasDebug = true
|
||||
case slog.LevelError, log.LevelCrit:
|
||||
hasError = true
|
||||
}
|
||||
}
|
||||
assert.True(t, hasDebug, "Expected a debug log when header is missing")
|
||||
assert.False(t, hasError, "Should not emit error-level logs for missing header")
|
||||
}
|
||||
|
||||
// TestVerifyVoteMessage_VoteRoundTooOld tests that votes with rounds below
|
||||
// the current round are rejected immediately
|
||||
func TestVerifyVoteMessage_VoteRoundTooOld(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -536,6 +536,32 @@ func TestVerifyVoteMsg(t *testing.T) {
|
|||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestVoteMsgMissingSnapshot(t *testing.T) {
|
||||
blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 915, params.TestXDPoSMockChainConfig, nil)
|
||||
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
||||
|
||||
blockInfo := &types.BlockInfo{
|
||||
Hash: currentBlock.Hash(),
|
||||
Round: types.Round(14),
|
||||
Number: big.NewInt(915),
|
||||
}
|
||||
voteForSign := &types.VoteForSign{
|
||||
ProposedBlockInfo: blockInfo,
|
||||
GapNumber: 450,
|
||||
}
|
||||
|
||||
signHash, _ := signFn(accounts.Account{Address: signer}, types.VoteSigHash(voteForSign).Bytes())
|
||||
voteMsg := &types.Vote{
|
||||
ProposedBlockInfo: blockInfo,
|
||||
Signature: signHash,
|
||||
GapNumber: 1350, // missing 1350 snapshot
|
||||
}
|
||||
engineV2.SetNewRoundFaker(blockchain, types.Round(14), false)
|
||||
verified, err := engineV2.VerifyVoteMessage(blockchain, voteMsg)
|
||||
assert.False(t, verified)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestVoteMessageHandlerWrongGapNumber(t *testing.T) {
|
||||
blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil)
|
||||
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
||||
|
|
|
|||
Loading…
Reference in a new issue