consensus: verify timeout from epochInfo instead of snap.NextEpochCandidates, close XFN-62 (#1628)

This commit is contained in:
Wanwiset Peerapatanapokin 2025-12-06 13:35:49 +04:00 committed by GitHub
parent fac866138b
commit e617598d1d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 33 additions and 55 deletions

View file

@ -78,6 +78,10 @@ func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types
log.Error("[getEpochSwitchInfo] get extra field", "err", err, "number", h.Number.Uint64())
return nil, err
}
if len(masternodes) == 0 {
return nil, fmt.Errorf("masternodes list is empty at epoch switch block %v", h.Number.Uint64())
}
snap, err := x.getSnapshot(chain, h.Number.Uint64(), false)
if err != nil {
log.Error("[getEpochSwitchInfo] Adaptor v2 getSnapshot has error", "err", err)
@ -110,11 +114,13 @@ func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types
x.epochSwitches.Add(hash, epochSwitchInfo)
return epochSwitchInfo, nil
}
epochSwitchInfo, err = x.getEpochSwitchInfo(chain, nil, h.ParentHash)
if err != nil {
log.Error("[getEpochSwitchInfo] recursive error", "err", err, "hash", hash.Hex(), "number", h.Number.Uint64())
return nil, err
}
log.Debug("[getEpochSwitchInfo] get epoch switch info recursively", "hash", hash.Hex(), "number", h.Number.Uint64())
x.epochSwitches.Add(hash, epochSwitchInfo)
return epochSwitchInfo, nil

View file

@ -31,9 +31,9 @@ func (x *XDPoS_v2) VerifySyncInfoMessage(chain consensus.ChainReader, syncInfo *
return false, nil
}
snapshot, err := x.getSnapshot(chain, qc.GapNumber, true)
epochInfo, err := x.getEpochSwitchInfo(chain, nil, qc.ProposedBlockInfo.Hash)
if err != nil {
log.Error("[VerifySyncInfoMessage] fail to get snapshot for a syncInfo message", "blockNum", qc.ProposedBlockInfo.Number, "blockHash", qc.ProposedBlockInfo.Hash, "error", err)
log.Error("[VerifySyncInfoMessage] fail to get epochInfo for qc syncInfo message", "blockNum", qc.ProposedBlockInfo.Number, "blockHash", qc.ProposedBlockInfo.Hash, "error", err)
return false, err
}
@ -42,16 +42,16 @@ func (x *XDPoS_v2) VerifySyncInfoMessage(chain consensus.ChainReader, syncInfo *
GapNumber: qc.GapNumber,
})
if err := x.verifySignatures(voteSigHash, qc.Signatures, snapshot.NextEpochCandidates); err != nil {
if err := x.verifySignatures(voteSigHash, qc.Signatures, epochInfo.Masternodes); err != nil {
log.Warn("[VerifySyncInfoMessage] SyncInfo message verification failed due to QC", "blockNum", qc.ProposedBlockInfo.Number, "gapNum", qc.GapNumber, "round", qc.ProposedBlockInfo.Round, "error", err)
return false, err
}
if tc != nil { // tc is optional, when the node is starting up there is no TC at the memory
snapshot, err = x.getSnapshot(chain, tc.GapNumber, true)
epochInfo, err := x.getTCEpochInfo(chain, tc.Round)
if err != nil {
log.Error("[VerifySyncInfoMessage] Fail to get snapshot when verifying TC!", "tcGapNumber", tc.GapNumber)
return false, fmt.Errorf("[VerifySyncInfoMessage] Unable to get snapshot, %s", err)
log.Error("[VerifySyncInfoMessage] Fail to get epochInfo for tc syncInfo message", "tcRound", tc.Round, "error", err)
return false, err
}
signedTimeoutObj := types.TimeoutSigHash(&types.TimeoutForSign{
@ -59,7 +59,7 @@ func (x *XDPoS_v2) VerifySyncInfoMessage(chain consensus.ChainReader, syncInfo *
GapNumber: tc.GapNumber,
})
if err := x.verifySignatures(signedTimeoutObj, tc.Signatures, snapshot.NextEpochCandidates); err != nil {
if err := x.verifySignatures(signedTimeoutObj, tc.Signatures, epochInfo.Masternodes); err != nil {
log.Warn("[VerifySyncInfoMessage] SyncInfo message verification failed due to TC", "gapNum", tc.GapNumber, "round", tc.Round, "error", err)
return false, err
}

View file

@ -22,20 +22,17 @@ func (x *XDPoS_v2) VerifyTimeoutMessage(chain consensus.ChainReader, timeoutMsg
log.Debug("[VerifyTimeoutMessage] Disqualified timeout message as the proposed round does not match currentRound", "timeoutHash", timeoutMsg.Hash(), "timeoutRound", timeoutMsg.Round, "currentRound", x.currentRound)
return false, nil
}
snap, err := x.getSnapshot(chain, timeoutMsg.GapNumber, true)
if err != nil || snap == nil {
log.Error("[VerifyTimeoutMessage] Fail to get snapshot when verifying timeout message!", "messageGapNumber", timeoutMsg.GapNumber, "err", err)
epochInfo, err := x.getTCEpochInfo(chain, timeoutMsg.Round)
if err != nil {
log.Error("[VerifyTimeoutMessage] Fail to get epochInfo for timeout message", "tcGapNumber", timeoutMsg.GapNumber, "tcRound", timeoutMsg.Round, "error", err)
return false, err
}
if len(snap.NextEpochCandidates) == 0 {
log.Error("[VerifyTimeoutMessage] cannot find NextEpochCandidates from snapshot", "messageGapNumber", timeoutMsg.GapNumber)
return false, errors.New("empty master node lists from snapshot")
}
verified, signer, err := x.verifyMsgSignature(types.TimeoutSigHash(&types.TimeoutForSign{
Round: timeoutMsg.Round,
GapNumber: timeoutMsg.GapNumber,
}), timeoutMsg.Signature, snap.NextEpochCandidates)
}), timeoutMsg.Signature, epochInfo.Masternodes)
if err != nil {
log.Warn("[VerifyTimeoutMessage] cannot verify timeout signature", "err", err)
@ -116,7 +113,7 @@ func (x *XDPoS_v2) onTimeoutPoolThresholdReached(blockChainReader consensus.Chai
return nil
}
func (x *XDPoS_v2) getTCEpochInfo(chain consensus.ChainReader, timeoutCert *types.TimeoutCert) (*types.EpochSwitchInfo, error) {
func (x *XDPoS_v2) getTCEpochInfo(chain consensus.ChainReader, timeoutRound types.Round) (*types.EpochSwitchInfo, error) {
epochSwitchInfo, err := x.getEpochSwitchInfo(chain, (chain.CurrentHeader()), (chain.CurrentHeader()).Hash())
if err != nil {
log.Error("[getTCEpochInfo] Error when getting epoch switch info", "error", err)
@ -131,18 +128,18 @@ func (x *XDPoS_v2) getTCEpochInfo(chain consensus.ChainReader, timeoutCert *type
Round: epochRound,
Number: epochSwitchInfo.EpochSwitchBlockInfo.Number,
}
log.Info("[getTCEpochInfo] Init epochInfo", "number", epochBlockInfo.Number, "round", epochRound, "tcRound", timeoutCert.Round, "tcEpoch", tempTCEpoch)
for epochBlockInfo.Round > timeoutCert.Round {
log.Info("[getTCEpochInfo] Init epochInfo", "number", epochBlockInfo.Number, "round", epochRound, "tcRound", timeoutRound, "tcEpoch", tempTCEpoch)
for epochBlockInfo.Round > timeoutRound {
tempTCEpoch--
epochBlockInfo, err = x.GetBlockByEpochNumber(chain, tempTCEpoch)
if err != nil {
log.Error("[getTCEpochInfo] Error when getting epoch block info by tc round", "error", err)
return nil, fmt.Errorf("fail on getTCEpochInfo due to failure in getting epoch block info tc round, %s", err)
}
log.Debug("[getTCEpochInfo] Loop to get right epochInfo", "number", epochBlockInfo.Number, "round", epochBlockInfo.Round, "tcRound", timeoutCert.Round, "tcEpoch", tempTCEpoch)
log.Debug("[getTCEpochInfo] Loop to get right epochInfo", "number", epochBlockInfo.Number, "round", epochBlockInfo.Round, "tcRound", timeoutRound, "tcEpoch", tempTCEpoch)
}
tcEpoch := tempTCEpoch
log.Info("[getTCEpochInfo] Final TC epochInfo", "number", epochBlockInfo.Number, "round", epochBlockInfo.Round, "tcRound", timeoutCert.Round, "tcEpoch", tcEpoch)
log.Info("[getTCEpochInfo] Final TC epochInfo", "number", epochBlockInfo.Number, "round", epochBlockInfo.Round, "tcRound", timeoutRound, "tcEpoch", tcEpoch)
epochInfo, err := x.getEpochSwitchInfo(chain, nil, epochBlockInfo.Hash)
if err != nil {
@ -175,7 +172,7 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time
}
}
epochInfo, err := x.getTCEpochInfo(chain, timeoutCert)
epochInfo, err := x.getTCEpochInfo(chain, timeoutCert.Round)
if err != nil {
return err
}

View file

@ -22,17 +22,18 @@ func (x *XDPoS_v2) VerifyVoteMessage(chain consensus.ChainReader, vote *types.Vo
return false, nil
}
snapshot, err := x.getSnapshot(chain, vote.GapNumber, true)
epochInfo, err := x.getEpochSwitchInfo(chain, chain.CurrentHeader(), chain.CurrentHeader().Hash())
if err != nil {
log.Error("[VerifyVoteMessage] fail to get snapshot for a vote message", "blockNum", vote.ProposedBlockInfo.Number, "blockHash", vote.ProposedBlockInfo.Hash, "voteHash", vote.Hash(), "error", err.Error())
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)
return false, err
}
verified, signer, err := x.verifyMsgSignature(types.VoteSigHash(&types.VoteForSign{
ProposedBlockInfo: vote.ProposedBlockInfo,
GapNumber: vote.GapNumber,
}), vote.Signature, snapshot.NextEpochCandidates)
}), vote.Signature, epochInfo.Masternodes)
if err != nil {
for i, mn := range snapshot.NextEpochCandidates {
for i, mn := range epochInfo.Masternodes {
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())

View file

@ -172,11 +172,11 @@ func TestGetBlockByEpochNumber(t *testing.T) {
blockchain, _, currentBlock, signer, signFn := PrepareXDCTestBlockChainWithPenaltyForV2Engine(t, 1802, params.TestXDPoSMockChainConfig)
blockCoinBase := "0x111000000000000000000000000000000123"
largeRound := int64(1802)
largeRound := int64(1800)
newBlock := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, int(currentBlock.NumberU64())+1, largeRound, blockCoinBase, signer, signFn, nil, nil, currentBlock.Header().Root.Hex())
err := blockchain.InsertBlock(newBlock)
assert.Nil(t, err)
largeRound2 := int64(3603)
largeRound2 := int64(3600)
newBlock2 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, newBlock, int(newBlock.NumberU64())+1, largeRound2, blockCoinBase, signer, signFn, nil, nil, newBlock.Header().Root.Hex())
err = blockchain.InsertBlock(newBlock2)
assert.Nil(t, err)
@ -189,9 +189,9 @@ func TestGetBlockByEpochNumber(t *testing.T) {
// 1800,900,2 (2nd epoch switch block)
// 1801,901,2
// 1802,902,2
// 1803,1802,3 (epoch switch)
// 1803,1800,3 (epoch switch)
// epoch 4 has no block
// 1804,3603,5 (epoch switch)
// 1804,3600,5 (epoch switch)
engine := blockchain.Engine().(*XDPoS.XDPoS)
// init the snapshot, otherwise getEpochSwitchInfo would return error

View file

@ -537,32 +537,6 @@ 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