mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-18 02:40:45 +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,
|
Number: epochSwitchInfo.EpochSwitchBlockInfo.Number,
|
||||||
}
|
}
|
||||||
log.Info("[getTCEpochInfo] Init epochInfo", "number", epochBlockInfo.Number, "round", epochRound, "tcRound", timeoutRound, "tcEpoch", tempTCEpoch)
|
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--
|
tempTCEpoch--
|
||||||
epochBlockInfo, err = x.GetBlockByEpochNumber(chain, tempTCEpoch)
|
epochBlockInfo, err = x.GetBlockByEpochNumber(chain, tempTCEpoch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -22,29 +22,22 @@ func (x *XDPoS_v2) VerifyVoteMessage(chain consensus.ChainReader, vote *types.Vo
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we don't yet have the referenced header locally, defer verification.
|
snap, err := x.getSnapshot(chain, vote.GapNumber, true)
|
||||||
// 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)
|
|
||||||
if err != nil {
|
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
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
verified, signer, err := x.verifyMsgSignature(types.VoteSigHash(&types.VoteForSign{
|
verified, signer, err := x.verifyMsgSignature(types.VoteSigHash(&types.VoteForSign{
|
||||||
ProposedBlockInfo: vote.ProposedBlockInfo,
|
ProposedBlockInfo: vote.ProposedBlockInfo,
|
||||||
GapNumber: vote.GapNumber,
|
GapNumber: vote.GapNumber,
|
||||||
}), vote.Signature, epochInfo.Masternodes)
|
}), vote.Signature, snap.NextEpochCandidates)
|
||||||
|
|
||||||
if err != nil {
|
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] 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
|
return false, err
|
||||||
}
|
}
|
||||||
vote.SetSigner(signer)
|
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)
|
verified, masterNode, err := x.verifyMsgSignature(signedVote, v.Signature, masternodes)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import (
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
@ -96,63 +95,6 @@ func (m *MockChainReader) GetBlock(hash common.Hash, number uint64) *types.Block
|
||||||
return nil
|
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
|
// TestVerifyVoteMessage_VoteRoundTooOld tests that votes with rounds below
|
||||||
// the current round are rejected immediately
|
// the current round are rejected immediately
|
||||||
func TestVerifyVoteMessage_VoteRoundTooOld(t *testing.T) {
|
func TestVerifyVoteMessage_VoteRoundTooOld(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -536,6 +536,32 @@ func TestVerifyVoteMsg(t *testing.T) {
|
||||||
assert.Nil(t, err)
|
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) {
|
func TestVoteMessageHandlerWrongGapNumber(t *testing.T) {
|
||||||
blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil)
|
blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil)
|
||||||
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue