From 572660612d5852e7840ed5dd1b633a0643b2b920 Mon Sep 17 00:00:00 2001 From: "liam.lai" Date: Tue, 19 Nov 2024 00:56:38 -0800 Subject: [PATCH 01/11] update version --- params/version.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/params/version.go b/params/version.go index 0162660a4c65..49c269f5c132 100644 --- a/params/version.go +++ b/params/version.go @@ -21,10 +21,10 @@ import ( ) const ( - VersionMajor = 2 // Major version component of the current release - VersionMinor = 3 // Minor version component of the current release - VersionPatch = 0 // Patch version component of the current release - VersionMeta = "stable" // Version metadata to append to the version string + VersionMajor = 2 // Major version component of the current release + VersionMinor = 4 // Minor version component of the current release + VersionPatch = 1 // Patch version component of the current release + VersionMeta = "beta1" // Version metadata to append to the version string ) // Version holds the textual version string. From 100ce5cbe5109f4376274bcb30d8c4e3addcd5ca Mon Sep 17 00:00:00 2001 From: "liam.lai" Date: Fri, 22 Nov 2024 00:01:22 -0800 Subject: [PATCH 02/11] bug fix for count vote threshold --- consensus/XDPoS/engines/engine_v2/vote.go | 4 ++-- params/version.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/consensus/XDPoS/engines/engine_v2/vote.go b/consensus/XDPoS/engines/engine_v2/vote.go index de79585297af..6e458c67a05b 100644 --- a/consensus/XDPoS/engines/engine_v2/vote.go +++ b/consensus/XDPoS/engines/engine_v2/vote.go @@ -76,7 +76,7 @@ func (x *XDPoS_v2) voteHandler(chain consensus.ChainReader, voteMsg *types.Vote) go x.ForensicsProcessor.DetectEquivocationInVotePool(voteMsg, x.votePool) go x.ForensicsProcessor.ProcessVoteEquivocation(chain, x, voteMsg) - epochInfo, err := x.getEpochSwitchInfo(chain, chain.CurrentHeader(), chain.CurrentHeader().Hash()) + epochInfo, err := x.getEpochSwitchInfo(chain, nil, voteMsg.ProposedBlockInfo.Hash) if err != nil { log.Error("[voteHandler] Error when getting epoch switch Info", "error", err) return errors.New("Fail on voteHandler due to failure in getting epoch switch info") @@ -175,7 +175,7 @@ func (x *XDPoS_v2) onVotePoolThresholdReached(chain consensus.ChainReader, poole } } - epochInfo, err := x.getEpochSwitchInfo(chain, chain.CurrentHeader(), chain.CurrentHeader().Hash()) + epochInfo, err := x.getEpochSwitchInfo(chain, nil, currentVoteMsg.(*types.Vote).ProposedBlockInfo.Hash) if err != nil { log.Error("[voteHandler] Error when getting epoch switch Info", "error", err) return errors.New("Fail on voteHandler due to failure in getting epoch switch info") diff --git a/params/version.go b/params/version.go index 49c269f5c132..4cc36519ec57 100644 --- a/params/version.go +++ b/params/version.go @@ -23,7 +23,7 @@ import ( const ( VersionMajor = 2 // Major version component of the current release VersionMinor = 4 // Minor version component of the current release - VersionPatch = 1 // Patch version component of the current release + VersionPatch = 2 // Patch version component of the current release VersionMeta = "beta1" // Version metadata to append to the version string ) From 21729181f7056dc23c753c25e459c5d8f618a77d Mon Sep 17 00:00:00 2001 From: "liam.lai" Date: Fri, 22 Nov 2024 18:36:53 -0800 Subject: [PATCH 03/11] increate timeout to 1 min for testnet --- params/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/params/config.go b/params/config.go index a7ac5ff3a4dc..73e54dd6c0f3 100644 --- a/params/config.go +++ b/params/config.go @@ -88,7 +88,7 @@ var ( SwitchRound: 0, CertThreshold: 0.45, TimeoutSyncThreshold: 3, - TimeoutPeriod: 20, + TimeoutPeriod: 60, MinePeriod: 2, }, 900000: { @@ -96,7 +96,7 @@ var ( SwitchRound: 900000, CertThreshold: 0.667, TimeoutSyncThreshold: 3, - TimeoutPeriod: 30, + TimeoutPeriod: 60, MinePeriod: 2, }, } From 92d239bf1dbd540a5d9710744bebafb52cb601e6 Mon Sep 17 00:00:00 2001 From: "liam.lai" Date: Fri, 13 Dec 2024 01:47:00 -0800 Subject: [PATCH 04/11] merge from master --- params/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/params/version.go b/params/version.go index 4cc36519ec57..a2aea26f2c21 100644 --- a/params/version.go +++ b/params/version.go @@ -23,7 +23,7 @@ import ( const ( VersionMajor = 2 // Major version component of the current release VersionMinor = 4 // Minor version component of the current release - VersionPatch = 2 // Patch version component of the current release + VersionPatch = 3 // Patch version component of the current release VersionMeta = "beta1" // Version metadata to append to the version string ) From 78331172f325c5e843f0558ab147caa662055655 Mon Sep 17 00:00:00 2001 From: benjamin202410 Date: Fri, 29 Nov 2024 03:14:25 -0800 Subject: [PATCH 05/11] cherry pick vote test fix --- consensus/XDPoS/engines/engine_v2/vote.go | 8 ++++++-- consensus/XDPoS/utils/errors.go | 13 +++++++++++++ consensus/tests/engine_v2_tests/vote_test.go | 11 +++++------ eth/bft/bft_handler.go | 4 ++++ eth/handler.go | 2 +- eth/hooks/engine_v2_hooks.go | 1 - 6 files changed, 29 insertions(+), 10 deletions(-) diff --git a/consensus/XDPoS/engines/engine_v2/vote.go b/consensus/XDPoS/engines/engine_v2/vote.go index 6e458c67a05b..4c1fb828d3c5 100644 --- a/consensus/XDPoS/engines/engine_v2/vote.go +++ b/consensus/XDPoS/engines/engine_v2/vote.go @@ -78,8 +78,12 @@ func (x *XDPoS_v2) voteHandler(chain consensus.ChainReader, voteMsg *types.Vote) epochInfo, err := x.getEpochSwitchInfo(chain, nil, voteMsg.ProposedBlockInfo.Hash) if err != nil { - log.Error("[voteHandler] Error when getting epoch switch Info", "error", err) - return errors.New("Fail on voteHandler due to failure in getting epoch switch info") + return &utils.ErrIncomingMessageBlockNotFound{ + Type: "vote", + IncomingBlockHash: voteMsg.ProposedBlockInfo.Hash, + IncomingBlockNumber: voteMsg.ProposedBlockInfo.Number, + Err: err, + } } certThreshold := x.config.V2.Config(uint64(voteMsg.ProposedBlockInfo.Round)).CertThreshold diff --git a/consensus/XDPoS/utils/errors.go b/consensus/XDPoS/utils/errors.go index 4a45d4157ab0..c6d0706ed853 100644 --- a/consensus/XDPoS/utils/errors.go +++ b/consensus/XDPoS/utils/errors.go @@ -3,7 +3,9 @@ package utils import ( "errors" "fmt" + "math/big" + "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/types" ) @@ -120,3 +122,14 @@ type ErrIncomingMessageRoundTooFarFromCurrentRound struct { func (e *ErrIncomingMessageRoundTooFarFromCurrentRound) Error() string { return fmt.Sprintf("%s message round number: %v is too far away from currentRound: %v", e.Type, e.IncomingRound, e.CurrentRound) } + +type ErrIncomingMessageBlockNotFound struct { + Type string + IncomingBlockHash common.Hash + IncomingBlockNumber *big.Int + Err error +} + +func (e *ErrIncomingMessageBlockNotFound) Error() string { + return fmt.Sprintf("%s proposed block is not found hash: %v, block number: %v, error: %s", e.Type, e.IncomingBlockHash.Hex(), e.IncomingBlockNumber, e.Err) +} diff --git a/consensus/tests/engine_v2_tests/vote_test.go b/consensus/tests/engine_v2_tests/vote_test.go index 53ac60eed1cd..35e9028f9c0c 100644 --- a/consensus/tests/engine_v2_tests/vote_test.go +++ b/consensus/tests/engine_v2_tests/vote_test.go @@ -10,7 +10,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/accounts" "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends" - "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/params" @@ -195,11 +194,11 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) { } func TestThrowErrorIfVoteMsgRoundIsMoreThanOneRoundAwayFromCurrentRound(t *testing.T) { - blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 blockInfo := &types.BlockInfo{ - Hash: common.HexToHash("0x1"), + Hash: currentBlock.Hash(), Round: types.Round(6), Number: big.NewInt(999), } @@ -397,7 +396,7 @@ func TestVoteMessageShallNotThrowErrorIfBlockNotYetExist(t *testing.T) { } err := engineV2.VoteHandler(blockchain, voteMsg) - assert.Nil(t, err) + assert.Contains(t, err.Error(), "proposed block is not found") voteMsg = &types.Vote{ ProposedBlockInfo: blockInfo, @@ -405,7 +404,7 @@ func TestVoteMessageShallNotThrowErrorIfBlockNotYetExist(t *testing.T) { GapNumber: 450, } err = engineV2.VoteHandler(blockchain, voteMsg) - assert.Nil(t, err) + assert.Contains(t, err.Error(), "proposed block is not found") // Create a vote message that should trigger vote pool hook, but it shall not produce any QC yet voteMsg = &types.Vote{ @@ -415,7 +414,7 @@ func TestVoteMessageShallNotThrowErrorIfBlockNotYetExist(t *testing.T) { } err = engineV2.VoteHandler(blockchain, voteMsg) - assert.Nil(t, err) + assert.Contains(t, err.Error(), "proposed block is not found") currentRound, lockQuorumCert, highestQuorumCert, _, _, _ := engineV2.GetPropertiesFaker() // Still using the initlised value because we did not yet go to the next round assert.Nil(t, lockQuorumCert) diff --git a/eth/bft/bft_handler.go b/eth/bft/bft_handler.go index 44d7c9b42378..267153dfb55d 100644 --- a/eth/bft/bft_handler.go +++ b/eth/bft/bft_handler.go @@ -101,6 +101,10 @@ func (b *Bfter) Vote(peer string, vote *types.Vote) error { log.Debug("vote round not equal", "error", err, "vote", vote.Hash()) return err } + if _, ok := err.(*utils.ErrIncomingMessageBlockNotFound); ok { + log.Debug("vote proposed block not found", "error", err, "vote", vote.Hash()) + return err + } log.Error("handle BFT Vote", "error", err) return err } diff --git a/eth/handler.go b/eth/handler.go index 76733fb8d863..c6c47503e1c8 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -863,7 +863,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error { if !exist { go pm.bft.Vote(p.id, &vote) } else { - log.Debug("Discarded vote, known vote", "vote hash", vote.Hash(), "voted block hash", vote.ProposedBlockInfo.Hash.Hex(), "number", vote.ProposedBlockInfo.Number, "round", vote.ProposedBlockInfo.Round) + log.Trace("Discarded vote, known vote", "vote hash", vote.Hash(), "voted block hash", vote.ProposedBlockInfo.Hash.Hex(), "number", vote.ProposedBlockInfo.Number, "round", vote.ProposedBlockInfo.Round) } case msg.Code == TimeoutMsg: diff --git a/eth/hooks/engine_v2_hooks.go b/eth/hooks/engine_v2_hooks.go index f356af27a74c..b725811b54ae 100644 --- a/eth/hooks/engine_v2_hooks.go +++ b/eth/hooks/engine_v2_hooks.go @@ -64,7 +64,6 @@ func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf parentNumber-- parentHash = parentHeader.ParentHash listBlockHash = append(listBlockHash, parentHash) - log.Debug("[HookPenalty] listBlockHash", "i", i, "len", len(listBlockHash), "parentHash", parentHash, "parentNumber", parentNumber) } // add list not miner to penalties From e1ab3e03d1e3ea560955e22c68c57a5c10a18f67 Mon Sep 17 00:00:00 2001 From: benjamin202410 Date: Thu, 19 Dec 2024 01:18:13 -0800 Subject: [PATCH 06/11] add buffer channel to unlock deadlock (#766) * add buffer channel to unlock deadlock * add buffer channel to unlock deadlock * close channel --------- Co-authored-by: liam.lai --- miner/worker.go | 7 +++++-- params/version.go | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/miner/worker.go b/miner/worker.go index df642c49bb5c..31bb721744ac 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -277,12 +277,15 @@ func (self *worker) update() { MinePeriodCh := self.engine.(*XDPoS.XDPoS).MinePeriodCh defer close(MinePeriodCh) NewRoundCh := self.engine.(*XDPoS.XDPoS).NewRoundCh + defer close(NewRoundCh) timeout := time.NewTimer(time.Duration(minePeriod) * time.Second) - c := make(chan struct{}) + defer timeout.Stop() + c := make(chan struct{}, 1) + defer close(c) finish := make(chan struct{}) defer close(finish) - defer timeout.Stop() + go func() { for { // A real event arrived, process interesting content diff --git a/params/version.go b/params/version.go index a2aea26f2c21..45407ecbb3e9 100644 --- a/params/version.go +++ b/params/version.go @@ -23,7 +23,7 @@ import ( const ( VersionMajor = 2 // Major version component of the current release VersionMinor = 4 // Minor version component of the current release - VersionPatch = 3 // Patch version component of the current release + VersionPatch = 4 // Patch version component of the current release VersionMeta = "beta1" // Version metadata to append to the version string ) From 50f401305972e993b92973b9dc97a576f7c38ba8 Mon Sep 17 00:00:00 2001 From: "liam.lai" Date: Fri, 20 Dec 2024 14:11:32 -0800 Subject: [PATCH 07/11] remove normal log --- consensus/XDPoS/engines/engine_v2/epochSwitch.go | 1 - 1 file changed, 1 deletion(-) diff --git a/consensus/XDPoS/engines/engine_v2/epochSwitch.go b/consensus/XDPoS/engines/engine_v2/epochSwitch.go index e9d76daa620a..8f718de0bec8 100644 --- a/consensus/XDPoS/engines/engine_v2/epochSwitch.go +++ b/consensus/XDPoS/engines/engine_v2/epochSwitch.go @@ -41,7 +41,6 @@ func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types log.Debug("[getEpochSwitchInfo] header doesn't provide, get header by hash", "hash", hash.Hex()) h = chain.GetHeaderByHash(hash) if h == nil { - log.Warn("[getEpochSwitchInfo] can not find header from db", "hash", hash.Hex()) return nil, fmt.Errorf("[getEpochSwitchInfo] can not find header from db hash %v", hash.Hex()) } } From 00785b096fa19443a90a8816be5d32324bd79ac4 Mon Sep 17 00:00:00 2001 From: "liam.lai" Date: Sun, 22 Dec 2024 19:34:09 -0800 Subject: [PATCH 08/11] fix tc cross epoch different masternode counts issue --- .../XDPoS/engines/engine_v2/epochSwitch.go | 2 +- consensus/XDPoS/engines/engine_v2/timeout.go | 22 ++- .../tests/engine_v2_tests/sync_info_test.go | 126 ++++++++++++++++++ core/types/consensus_v2.go | 6 +- 4 files changed, 145 insertions(+), 11 deletions(-) diff --git a/consensus/XDPoS/engines/engine_v2/epochSwitch.go b/consensus/XDPoS/engines/engine_v2/epochSwitch.go index 8f718de0bec8..907f1071990f 100644 --- a/consensus/XDPoS/engines/engine_v2/epochSwitch.go +++ b/consensus/XDPoS/engines/engine_v2/epochSwitch.go @@ -152,7 +152,7 @@ func (x *XDPoS_v2) IsEpochSwitch(header *types.Header) (bool, uint64, error) { log.Info("[IsEpochSwitch] true, parent equals V2.SwitchBlock", "round", round, "number", header.Number.Uint64(), "hash", header.Hash()) return true, epochNum, nil } - log.Debug("[IsEpochSwitch]", "is", parentRound < epochStartRound, "parentRound", parentRound, "round", round, "number", header.Number.Uint64(), "epochNum", epochNum, "hash", header.Hash()) + log.Debug("[IsEpochSwitch]", "is", parentRound < epochStartRound, "parentRound", parentRound, "round", round, "number", header.Number.Uint64(), "epochNum", epochNum, "hash", header.Hash().Hex()) // if isEpochSwitch, add to cache if parentRound < epochStartRound { x.round2epochBlockInfo.Add(round, &types.BlockInfo{ diff --git a/consensus/XDPoS/engines/engine_v2/timeout.go b/consensus/XDPoS/engines/engine_v2/timeout.go index 2f077b2a0018..86647850c362 100644 --- a/consensus/XDPoS/engines/engine_v2/timeout.go +++ b/consensus/XDPoS/engines/engine_v2/timeout.go @@ -95,7 +95,7 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time snap, err := x.getSnapshot(chain, timeoutCert.GapNumber, true) if err != nil { - log.Error("[verifyTC] Fail to get snapshot when verifying TC!", "TCGapNumber", timeoutCert.GapNumber) + log.Error("[verifyTC] Fail to get snapshot when verifying TC!", "tcGapNumber", timeoutCert.GapNumber) return fmt.Errorf("[verifyTC] Unable to get snapshot, %s", err) } if snap == nil || len(snap.NextEpochCandidates) == 0 { @@ -109,16 +109,22 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time log.Warn("[verifyQC] duplicated signature in QC", "duplicate", common.Bytes2Hex(d)) } } + tcEpoch := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(timeoutCert.Round)/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) + return fmt.Errorf("fail on verifyTC due to failure in getting epoch block info tc round, %s", err) + } - epochInfo, err := x.getEpochSwitchInfo(chain, chain.CurrentHeader(), chain.CurrentHeader().Hash()) + epochInfo, err := x.getEpochSwitchInfo(chain, nil, epochBlockInfo.Hash) if err != nil { - log.Error("[verifyTC] Error when getting epoch switch Info", "error", err) + log.Error("[verifyTC] Error when getting epoch switch info", "error", err) return fmt.Errorf("fail on verifyTC due to failure in getting epoch switch info, %s", err) } certThreshold := x.config.V2.Config(uint64(timeoutCert.Round)).CertThreshold if float64(len(signatures)) < float64(epochInfo.MasternodesLen)*certThreshold { - log.Warn("[verifyTC] Invalid TC Signature is nil or empty", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(timeoutCert.Signatures), "CertThreshold", float64(epochInfo.MasternodesLen)*certThreshold) + log.Warn("[verifyTC] Invalid TC Signature is less or empty", "tcRound", timeoutCert.Round, "tcGapNumber", timeoutCert.GapNumber, "tcSignLen", len(timeoutCert.Signatures), "certThreshold", float64(epochInfo.MasternodesLen)*certThreshold) return utils.ErrInvalidTCSignatures } @@ -138,14 +144,14 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time defer wg.Done() verified, _, err := x.verifyMsgSignature(signedTimeoutObj, sig, snap.NextEpochCandidates) if err != nil || !verified { - log.Error("[verifyTC] Error or verification failure", "Signature", sig, "Error", err) + log.Error("[verifyTC] Error or verification failure", "signature", sig, "error", err) mutex.Lock() // Lock before accessing haveError if haveError == nil { if err != nil { - log.Error("[verifyTC] Error while verfying TC message signatures", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(signatures), "Error", err) + log.Error("[verifyTC] Error while verfying TC message signatures", "tcRound", timeoutCert.Round, "tcGapNumber", timeoutCert.GapNumber, "tcSignLen", len(signatures), "error", err) haveError = fmt.Errorf("error while verifying TC message signatures, %s", err) } else { - log.Warn("[verifyTC] Signature not verified doing TC verification", "timeoutCert.Round", timeoutCert.Round, "timeoutCert.GapNumber", timeoutCert.GapNumber, "Signatures len", len(signatures)) + log.Warn("[verifyTC] Signature not verified doing TC verification", "tcRound", timeoutCert.Round, "tcGapNumber", timeoutCert.GapNumber, "tcSignLen", len(signatures)) haveError = errors.New("fail to verify TC due to signature mis-match") } } @@ -219,6 +225,8 @@ func (x *XDPoS_v2) sendTimeout(chain consensus.ChainReader) error { Signature: signedHash, GapNumber: gapNumber, } + + timeoutMsg.SetSigner(x.signer) log.Warn("[sendTimeout] Timeout message generated, ready to send!", "timeoutMsgRound", timeoutMsg.Round, "timeoutMsgGapNumber", timeoutMsg.GapNumber, "whosTurn", x.whosTurn) err = x.timeoutHandler(chain, timeoutMsg) if err != nil { diff --git a/consensus/tests/engine_v2_tests/sync_info_test.go b/consensus/tests/engine_v2_tests/sync_info_test.go index 8219b37a4e15..e3ac910b235b 100644 --- a/consensus/tests/engine_v2_tests/sync_info_test.go +++ b/consensus/tests/engine_v2_tests/sync_info_test.go @@ -1,9 +1,11 @@ package engine_v2_tests import ( + "fmt" "math/big" "testing" + "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" "github.com/XinFinOrg/XDPoSChain/core/types" @@ -100,3 +102,127 @@ func TestSkipVerifySyncInfoIfBothQcTcNotQualified(t *testing.T) { assert.False(t, verified) assert.Nil(t, err) } + +func TestVerifySyncInfoIfTcUseDifferentEpoch(t *testing.T) { + config := params.TestXDPoSMockChainConfig + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 1349, config, nil) + adaptor := blockchain.Engine().(*XDPoS.XDPoS) + x := adaptor.EngineV2 + + // Insert block 1350 + t.Logf("Inserting block with propose at 1350...") + blockCoinbaseA := "0xaaa0000000000000000000000000000000001350" + // NOTE: voterAddr never exist in the Masternode list, but all acc1,2,3 already does + tx, err := voteTX(37117, 0, signer.String()) + if err != nil { + t.Fatal(err) + } + //Get from block validator error message + merkleRoot := "8a355a8636d1aae24d5a63df0318534e09110891d6ab7bf20587da64725083be" + header := &types.Header{ + Root: common.HexToHash(merkleRoot), + Number: big.NewInt(int64(1350)), + ParentHash: currentBlock.Hash(), + Coinbase: common.HexToAddress(blockCoinbaseA), + } + + header.Extra = generateV2Extra(450, currentBlock, signer, signFn, nil) + + parentBlock, err := createBlockFromHeader(blockchain, header, []*types.Transaction{tx}, signer, signFn, config) + assert.Nil(t, err) + err = blockchain.InsertBlock(parentBlock) + assert.Nil(t, err) + // 1350 is a gap block, need to update the snapshot + err = blockchain.UpdateM1() + assert.Nil(t, err) + t.Logf("Inserting block from 1351 to 1799...") + for i := 1351; i <= 1799; i++ { + blockCoinbase := fmt.Sprintf("0xaaa000000000000000000000000000000000%4d", i) + //Get from block validator error message + header = &types.Header{ + Root: common.HexToHash(merkleRoot), + Number: big.NewInt(int64(i)), + ParentHash: parentBlock.Hash(), + Coinbase: common.HexToAddress(blockCoinbase), + } + + header.Extra = generateV2Extra(int64(i)-900, parentBlock, signer, signFn, nil) + + block, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, config) + if err != nil { + t.Fatal(err) + } + err = blockchain.InsertBlock(block) + assert.Nil(t, err) + parentBlock = block + } + t.Logf("build epoch block with new set of masternodes") + blockCoinbase := fmt.Sprintf("0xaaa0000000000000000000000000000000001800") + //Get from block validator error message + header = &types.Header{ + Root: common.HexToHash(merkleRoot), + Number: big.NewInt(int64(1800)), + ParentHash: parentBlock.Hash(), + Coinbase: common.HexToAddress(blockCoinbase), + } + + header.Extra = generateV2Extra(900, parentBlock, signer, signFn, nil) + validators := []byte{} + + snap, err := x.GetSnapshot(blockchain, parentBlock.Header()) + assert.Nil(t, err) + + for _, v := range snap.NextEpochCandidates { + validators = append(validators, v[:]...) + } + // set up 1 more masternode to make it difference + validators = append(validators, voterAddr[:]...) + header.Validators = validators + block, err := createBlockFromHeader(blockchain, header, nil, signer, signFn, config) + if err != nil { + t.Fatal(err) + } + err = blockchain.InsertBlock(block) + assert.Nil(t, err) + parentBlock = block + + var extraField types.ExtraFields_v2 + err = utils.DecodeBytesExtraFields(parentBlock.Extra(), &extraField) + if err != nil { + t.Fatal("Fail to decode extra data", err) + } + + timeoutForSign := &types.TimeoutForSign{ + Round: types.Round(899), + 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) + + newTC := &types.TimeoutCert{ + Round: timeoutForSign.Round, + Signatures: signatures, + GapNumber: timeoutForSign.GapNumber, + } + + syncInfoMsg := &types.SyncInfo{ + HighestQuorumCert: extraField.QuorumCert, + HighestTimeoutCert: newTC, + } + + x.SetPropertiesFaker(syncInfoMsg.HighestQuorumCert, &types.TimeoutCert{ + Round: types.Round(898), + Signatures: []types.Signature{}, + }) + + verified, err := x.VerifySyncInfoMessage(blockchain, syncInfoMsg) + assert.True(t, verified) + assert.Nil(t, err) +} diff --git a/core/types/consensus_v2.go b/core/types/consensus_v2.go index 8dae6aaa0728..f69892532dea 100644 --- a/core/types/consensus_v2.go +++ b/core/types/consensus_v2.go @@ -21,7 +21,7 @@ type BlockInfo struct { // Vote message in XDPoS 2.0 type Vote struct { - signer common.Address //field not exported + signer common.Address //field not exported ProposedBlockInfo *BlockInfo `json:"proposedBlockInfo"` Signature Signature `json:"signature"` GapNumber uint64 `json:"gapNumber"` @@ -81,9 +81,9 @@ func (s *SyncInfo) Hash() common.Hash { // Quorum Certificate struct in XDPoS 2.0 type QuorumCert struct { - ProposedBlockInfo *BlockInfo `json:"proposedBlockInfo"` + ProposedBlockInfo *BlockInfo `json:"proposedBlockInfo"` Signatures []Signature `json:"signatures"` - GapNumber uint64 `json:"gapNumber"` + GapNumber uint64 `json:"gapNumber"` } // Timeout Certificate struct in XDPoS 2.0 From 25462bde4ca405306b6a79e66bbac4ed22fb8d5e Mon Sep 17 00:00:00 2001 From: "liam.lai" Date: Sun, 22 Dec 2024 19:34:49 -0800 Subject: [PATCH 09/11] fix tc cross epoch different masternode counts issue --- params/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/params/version.go b/params/version.go index 45407ecbb3e9..23a1aad9acc8 100644 --- a/params/version.go +++ b/params/version.go @@ -23,7 +23,7 @@ import ( const ( VersionMajor = 2 // Major version component of the current release VersionMinor = 4 // Minor version component of the current release - VersionPatch = 4 // Patch version component of the current release + VersionPatch = 5 // Patch version component of the current release VersionMeta = "beta1" // Version metadata to append to the version string ) From 332380c8894c51c1557ae489669577f30f979398 Mon Sep 17 00:00:00 2001 From: benjamin202410 Date: Mon, 23 Dec 2024 23:09:45 -0800 Subject: [PATCH 10/11] fix only timeout round exists in next epoch (#772) * fix only timeout round exists in next epoch * bump version --------- Co-authored-by: liam.lai --- consensus/XDPoS/engines/engine_v2/timeout.go | 12 +++++ .../tests/engine_v2_tests/sync_info_test.go | 49 +++++++++++++++++++ params/version.go | 2 +- 3 files changed, 62 insertions(+), 1 deletion(-) 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) diff --git a/params/version.go b/params/version.go index 23a1aad9acc8..6dad86457668 100644 --- a/params/version.go +++ b/params/version.go @@ -23,7 +23,7 @@ import ( const ( VersionMajor = 2 // Major version component of the current release VersionMinor = 4 // Minor version component of the current release - VersionPatch = 5 // Patch version component of the current release + VersionPatch = 6 // Patch version component of the current release VersionMeta = "beta1" // Version metadata to append to the version string ) From c8aae5e53752ca32e67ada9ed334220f18a7c27d Mon Sep 17 00:00:00 2001 From: benjamin202410 Date: Tue, 24 Dec 2024 01:53:40 -0800 Subject: [PATCH 11/11] Get right tc epoch (#773) * get right tc epoch * fix debug message * merge test * merge test --------- Co-authored-by: liam.lai --- .github/workflows/ci.yml | 6 ++-- consensus/XDPoS/engines/engine_v2/timeout.go | 33 +++++++++++++------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2c41c2b2a65f..64dfde9142b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,10 +42,8 @@ jobs: script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[d-i].*") - name: J-N tests script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[j-n].*") - - name: O-R tests - script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[o-r].*") - - name: S tests - script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/s.*") + - name: O-S tests + script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[o-s].*") - name: T-Z tests script: go run build/ci.go test -coverage $(go list ./... | grep "github.com/XinFinOrg/XDPoSChain/[t-z].*") steps: diff --git a/consensus/XDPoS/engines/engine_v2/timeout.go b/consensus/XDPoS/engines/engine_v2/timeout.go index 4a9c8d3017b0..875dd22c9c99 100644 --- a/consensus/XDPoS/engines/engine_v2/timeout.go +++ b/consensus/XDPoS/engines/engine_v2/timeout.go @@ -109,24 +109,33 @@ 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()) + + epochSwitchInfo, err := x.getEpochSwitchInfo(chain, (chain.CurrentHeader()), (chain.CurrentHeader()).Hash()) 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) + log.Error("[verifyTC] Error when getting epoch switch info", "error", err) + return fmt.Errorf("fail on verifyTC due to failure in getting epoch switch info, %s", err) } - tcEpoch := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(timeoutCert.Round)/x.config.Epoch + epochRound := epochSwitchInfo.EpochSwitchBlockInfo.Round + tempTCEpoch := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(epochRound)/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 := &types.BlockInfo{ + Hash: epochSwitchInfo.EpochSwitchBlockInfo.Hash, + Round: epochRound, + Number: epochSwitchInfo.EpochSwitchBlockInfo.Number, } - - epochBlockInfo, err := x.GetBlockByEpochNumber(chain, tcEpoch) - if err != nil { - log.Error("[verifyTC] Error when getting epoch block info by tc round", "error", err) - return fmt.Errorf("fail on verifyTC due to failure in getting epoch block info tc round, %s", err) + log.Info("[verifyTC] Init epochInfo", "number", epochBlockInfo.Number, "round", epochRound, "tcRound", timeoutCert.Round, "tcEpoch", tempTCEpoch) + for epochBlockInfo.Round > timeoutCert.Round { + tempTCEpoch-- + epochBlockInfo, err = x.GetBlockByEpochNumber(chain, tempTCEpoch) + if err != nil { + log.Error("[verifyTC] Error when getting epoch block info by tc round", "error", err) + return fmt.Errorf("fail on verifyTC due to failure in getting epoch block info tc round, %s", err) + } + log.Debug("[verifyTC] Loop to get right epochInfo", "number", epochBlockInfo.Number, "round", epochBlockInfo.Round, "tcRound", timeoutCert.Round, "tcEpoch", tempTCEpoch) } + tcEpoch := tempTCEpoch + log.Info("[verifyTC] Final TC epochInfo", "number", epochBlockInfo.Number, "round", epochBlockInfo.Round, "tcRound", timeoutCert.Round, "tcEpoch", tcEpoch) epochInfo, err := x.getEpochSwitchInfo(chain, nil, epochBlockInfo.Hash) if err != nil {