From d318e20d25b0a665658ef0f7a77e9e3305b95692 Mon Sep 17 00:00:00 2001 From: "liam.lai" Date: Mon, 23 Dec 2024 22:55:58 -0800 Subject: [PATCH] fix only timeout round exists in next epoch --- consensus/XDPoS/engines/engine_v2/timeout.go | 12 +++++ .../tests/engine_v2_tests/sync_info_test.go | 49 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/consensus/XDPoS/engines/engine_v2/timeout.go b/consensus/XDPoS/engines/engine_v2/timeout.go index 86647850c362..4a9c8d3017b0 100644 --- a/consensus/XDPoS/engines/engine_v2/timeout.go +++ b/consensus/XDPoS/engines/engine_v2/timeout.go @@ -109,7 +109,19 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time log.Warn("[verifyQC] duplicated signature in QC", "duplicate", common.Bytes2Hex(d)) } } + latestBlockRound, err := x.GetRoundNumber(chain.CurrentHeader()) + if err != nil { + log.Error("[verifyTC] Error when getting current header round", "error", err) + return fmt.Errorf("fail on verifyTC due to error when getting current header round, %s", err) + } + tcEpoch := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(timeoutCert.Round)/x.config.Epoch + + //tcEpoch maybe not existed if there is no QC round in this epoch, there is no epoch switch block generated, so it needs to use currentRound to find epochBlockInfo + if latestBlockRound < timeoutCert.Round { + tcEpoch = x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(latestBlockRound)/x.config.Epoch + } + epochBlockInfo, err := x.GetBlockByEpochNumber(chain, tcEpoch) if err != nil { log.Error("[verifyTC] Error when getting epoch block info by tc round", "error", err) diff --git a/consensus/tests/engine_v2_tests/sync_info_test.go b/consensus/tests/engine_v2_tests/sync_info_test.go index e3ac910b235b..ebe2e4db6873 100644 --- a/consensus/tests/engine_v2_tests/sync_info_test.go +++ b/consensus/tests/engine_v2_tests/sync_info_test.go @@ -103,6 +103,55 @@ func TestSkipVerifySyncInfoIfBothQcTcNotQualified(t *testing.T) { assert.Nil(t, err) } +func TestVerifySyncInfoIfTCRoundIsAtNextEpoch(t *testing.T) { + blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) + engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 + + // Make the Highest QC in syncInfo point to an old block to simulate it's no longer qualified + parentBlock := blockchain.GetBlockByNumber(903) + var extraField types.ExtraFields_v2 + err := utils.DecodeBytesExtraFields(parentBlock.Extra(), &extraField) + if err != nil { + t.Fatal("Fail to decode extra data", err) + } + + highestTC := &types.TimeoutCert{ + Round: types.Round(899), + Signatures: []types.Signature{}, + } + + timeoutForSign := &types.TimeoutForSign{ + Round: types.Round(900), + GapNumber: 450, + } + + // Sign from acc 1, 2, 3 and voter + acc1SignedHash := SignHashByPK(acc1Key, types.TimeoutSigHash(timeoutForSign).Bytes()) + acc2SignedHash := SignHashByPK(acc2Key, types.TimeoutSigHash(timeoutForSign).Bytes()) + acc3SignedHash := SignHashByPK(acc3Key, types.TimeoutSigHash(timeoutForSign).Bytes()) + voterSignedHash := SignHashByPK(voterKey, types.TimeoutSigHash(timeoutForSign).Bytes()) + + var signatures []types.Signature + signatures = append(signatures, acc1SignedHash, acc2SignedHash, acc3SignedHash, voterSignedHash) + + syncInfoTC := &types.TimeoutCert{ + Round: timeoutForSign.Round, + Signatures: signatures, + GapNumber: timeoutForSign.GapNumber, + } + + syncInfoMsg := &types.SyncInfo{ + HighestQuorumCert: extraField.QuorumCert, + HighestTimeoutCert: syncInfoTC, + } + + engineV2.SetPropertiesFaker(syncInfoMsg.HighestQuorumCert, highestTC) + + verified, err := engineV2.VerifySyncInfoMessage(blockchain, syncInfoMsg) + assert.True(t, verified) + assert.Nil(t, err) +} + func TestVerifySyncInfoIfTcUseDifferentEpoch(t *testing.T) { config := params.TestXDPoSMockChainConfig blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 1349, config, nil)