From 2a94cdebe59f6f3209ecc9f3e2cc9643bc925eb7 Mon Sep 17 00:00:00 2001 From: Jianrong Date: Sun, 12 Dec 2021 00:15:59 +1100 Subject: [PATCH] XIN-109: Add more proposed block handler tests --- consensus/XDPoS/engines/engine_v2/engine.go | 13 +- consensus/tests/adaptor_test.go | 7 +- consensus/tests/countdown_test.go | 2 +- consensus/tests/proposed_block_test.go | 168 ++++++++++++++++++-- consensus/tests/test_helper.go | 78 ++++++--- consensus/tests/timeout_test.go | 10 +- consensus/tests/vote_test.go | 149 +++++++++++------ 7 files changed, 335 insertions(+), 92 deletions(-) diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index 1cf768a9fa..98288c2d30 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -437,10 +437,10 @@ func (x *XDPoS_v2) VerifyHeader(chain consensus.ChainReader, header *types.Heade } // Utils for test to check currentRound value -func (x *XDPoS_v2) GetProperties() (utils.Round, *utils.QuorumCert, *utils.QuorumCert) { +func (x *XDPoS_v2) GetProperties() (utils.Round, *utils.QuorumCert, *utils.QuorumCert, utils.Round) { x.lock.Lock() defer x.lock.Unlock() - return x.currentRound, x.lockQuorumCert, x.highestQuorumCert + return x.currentRound, x.lockQuorumCert, x.highestQuorumCert, x.highestVotedRound } /* @@ -569,7 +569,9 @@ func (x *XDPoS_v2) TimeoutHandler(timeout *utils.Timeout) error { // 1. checkRoundNumber if timeout.Round != x.currentRound { - return &utils.ErrIncomingMessageRoundNotEqualCurrentRound{timeout.Round, x.currentRound} + return &utils.ErrIncomingMessageRoundNotEqualCurrentRound{ + IncomingRound: timeout.Round, + CurrentRound: x.currentRound} } // Collect timeout, generate TC isThresholdReached, numberOfTimeoutsInPool, pooledTimeouts := x.timeoutPool.Add(timeout) @@ -839,11 +841,6 @@ func (x *XDPoS_v2) sendTimeout() error { return nil } -// Generate and send syncInfo into Broadcast channel. The SyncInfo includes local highest QC & TC -func (x *XDPoS_v2) sendSyncInfo() error { - return nil -} - func (x *XDPoS_v2) signSignature(signingHash common.Hash) (utils.Signature, error) { // Don't hold the signFn for the whole signing operation x.signLock.RLock() diff --git a/consensus/tests/adaptor_test.go b/consensus/tests/adaptor_test.go index eae5ce9d86..6d27903818 100644 --- a/consensus/tests/adaptor_test.go +++ b/consensus/tests/adaptor_test.go @@ -13,7 +13,7 @@ import ( ) func TestAdaptorShouldGetAuthorForDifferentConsensusVersion(t *testing.T) { - blockchain, backend, currentBlock, _ := PrepareXDCTestBlockChainForV2Engine(t, 10, params.TestXDPoSMockChainConfigWithV2Engine) + blockchain, backend, currentBlock, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 10, params.TestXDPoSMockChainConfigWithV2Engine, 0) adaptor := blockchain.Engine().(*XDPoS.XDPoS) addressFromAdaptor, errorAdaptor := adaptor.Author(currentBlock.Header()) @@ -38,7 +38,10 @@ func TestAdaptorShouldGetAuthorForDifferentConsensusVersion(t *testing.T) { ParentHash: currentBlock.Hash(), Coinbase: common.HexToAddress(blockCoinBase), } - generateSignature(backend, header) + err := generateSignature(backend, header) + if err != nil { + t.Fatal(err) + } block11, err := insertBlock(blockchain, header) if err != nil { t.Fatal(err) diff --git a/consensus/tests/countdown_test.go b/consensus/tests/countdown_test.go index cfc4c373c8..3f191a86cb 100644 --- a/consensus/tests/countdown_test.go +++ b/consensus/tests/countdown_test.go @@ -10,7 +10,7 @@ import ( ) func TestCountdownTimeoutToSendTimeoutMessage(t *testing.T) { - blockchain, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine) + blockchain, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine, 0) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 engineV2.SetNewRoundFaker(utils.Round(1), true) diff --git a/consensus/tests/proposed_block_test.go b/consensus/tests/proposed_block_test.go index dc76d9f22e..6ec376fb2f 100644 --- a/consensus/tests/proposed_block_test.go +++ b/consensus/tests/proposed_block_test.go @@ -2,6 +2,7 @@ package tests import ( "testing" + "time" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" @@ -9,14 +10,11 @@ import ( "github.com/stretchr/testify/assert" ) -// ProposeBlock handler -func TestProposedBlockMessageHandlerSuccessfullyGenerateVote(t *testing.T) { - blockchain, _, currentBlock, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine) +func TestProcessFirstV2BlockAndSendVoteMsg(t *testing.T) { + // Block 11 is the first v2 block with starting round of 0 + blockchain, _, currentBlock, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine, 0) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 - // Set round to 11 - engineV2.SetNewRoundFaker(utils.Round(11), false) - var extraField utils.ExtraFields_v2 err := utils.DecodeBytesExtraFields(currentBlock.Extra(), &extraField) if err != nil { @@ -32,8 +30,160 @@ func TestProposedBlockMessageHandlerSuccessfullyGenerateVote(t *testing.T) { assert.NotNil(t, voteMsg) assert.Equal(t, currentBlock.Hash(), voteMsg.(*utils.Vote).ProposedBlockInfo.Hash) - round, _, highestQC := engineV2.GetProperties() - // Shoud not trigger setNewRound - assert.Equal(t, utils.Round(11), round) + round, _, highestQC, _ := engineV2.GetProperties() + // Shoud trigger setNewRound + assert.Equal(t, utils.Round(1), round) + assert.Equal(t, extraField.QuorumCert.Signatures, highestQC.Signatures) + +} + +func TestProposedBlockMessageHandlerSuccessfullyGenerateVote(t *testing.T) { + blockchain, _, currentBlock, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 16, params.TestXDPoSMockChainConfigWithV2Engine, 0) + engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 + + // Set current round to 5 + engineV2.SetNewRoundFaker(utils.Round(5), false) + + var extraField utils.ExtraFields_v2 + err := utils.DecodeBytesExtraFields(currentBlock.Extra(), &extraField) + if err != nil { + t.Fatal("Fail to decode extra data", err) + } + + err = engineV2.ProposedBlockHandler(blockchain, currentBlock.Header()) + if err != nil { + t.Fatal("Fail propose proposedBlock handler", err) + } + + voteMsg := <-engineV2.BroadcastCh + assert.NotNil(t, voteMsg) + assert.Equal(t, currentBlock.Hash(), voteMsg.(*utils.Vote).ProposedBlockInfo.Hash) + + round, _, highestQC, _ := engineV2.GetProperties() + // Shoud trigger setNewRound + assert.Equal(t, utils.Round(6), round) assert.Equal(t, extraField.QuorumCert.Signatures, highestQC.Signatures) } + +// Should not set new round if proposedBlockInfo round is less than currentRound. +// NOTE: This shall not even happen because we have `verifyQC` before being passed into ProposedBlockHandler +func TestShouldNotSetNewRound(t *testing.T) { + blockchain, _, currentBlock, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 16, params.TestXDPoSMockChainConfigWithV2Engine, 0) + engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 + + // Set current round to 6 + engineV2.SetNewRoundFaker(utils.Round(6), false) + + var extraField utils.ExtraFields_v2 + err := utils.DecodeBytesExtraFields(currentBlock.Extra(), &extraField) + if err != nil { + t.Fatal("Fail to decode extra data", err) + } + + err = engineV2.ProposedBlockHandler(blockchain, currentBlock.Header()) + if err != nil { + t.Fatal("Fail propose proposedBlock handler", err) + } + + round, _, highestQC, _ := engineV2.GetProperties() + // Shoud not trigger setNewRound + assert.Equal(t, utils.Round(6), round) + assert.Equal(t, extraField.QuorumCert.Signatures, highestQC.Signatures) +} + +func TestShouldNotSendVoteMessageIfAlreadyVoteForThisRound(t *testing.T) { + blockchain, _, currentBlock, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 16, params.TestXDPoSMockChainConfigWithV2Engine, 0) + engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 + + // Set current round to 5 + engineV2.SetNewRoundFaker(utils.Round(5), false) + + err := engineV2.ProposedBlockHandler(blockchain, currentBlock.Header()) + if err != nil { + t.Fatal("Fail propose proposedBlock handler", err) + } + + voteMsg := <-engineV2.BroadcastCh + assert.NotNil(t, voteMsg) + assert.Equal(t, currentBlock.Hash(), voteMsg.(*utils.Vote).ProposedBlockInfo.Hash) + + round, _, _, highestVotedRound := engineV2.GetProperties() + // Shoud trigger setNewRound + assert.Equal(t, utils.Round(6), round) + assert.Equal(t, utils.Round(6), highestVotedRound) + + // Let's send again, this time, it shall not broadcast any vote message, because HigestVoteRound is same as currentRound + err = engineV2.ProposedBlockHandler(blockchain, currentBlock.Header()) + if err != nil { + t.Fatal("Fail propose proposedBlock handler again", err) + } + // Should not receive anything from the channel + select { + case <-engineV2.BroadcastCh: + t.Fatal("Should not trigger vote") + case <-time.After(5 * time.Second): + // Shoud not trigger setNewRound + round, _, _, highestVotedRound = engineV2.GetProperties() + assert.Equal(t, utils.Round(6), round) + assert.Equal(t, utils.Round(6), highestVotedRound) + } +} + +func TestShouldNotSendVoteMsgIfBlockInfoRoundNotEqualCurrentRound(t *testing.T) { + blockchain, _, currentBlock, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 16, params.TestXDPoSMockChainConfigWithV2Engine, 0) + engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 + + // Set current round to 8 + engineV2.SetNewRoundFaker(utils.Round(8), false) + + var extraField utils.ExtraFields_v2 + err := utils.DecodeBytesExtraFields(currentBlock.Extra(), &extraField) + if err != nil { + t.Fatal("Fail to decode extra data", err) + } + + err = engineV2.ProposedBlockHandler(blockchain, currentBlock.Header()) + if err != nil { + t.Fatal("Fail propose proposedBlock handler", err) + } + // Should not receive anything from the channel + select { + case <-engineV2.BroadcastCh: + t.Fatal("Should not trigger vote") + case <-time.After(5 * time.Second): + // Shoud not trigger setNewRound + round, _, _, _ := engineV2.GetProperties() + assert.Equal(t, utils.Round(8), round) + } +} + +// TODO: Uncomment once confirmed the lockQuorumCert usage +// func TestShouldNotSendVoteMsgIfBlockNotExtendedFromAncestor(t *testing.T) { +// blockchain, _, currentBlock, _, forkedBlock := PrepareXDCTestBlockChainForV2Engine(t, 16, params.TestXDPoSMockChainConfigWithV2Engine, 16) +// engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 + +// var extraField utils.ExtraFields_v2 +// err := utils.DecodeBytesExtraFields(forkedBlock.Extra(), &extraField) +// if err != nil { +// t.Fatal("Fail to decode extra data", err) +// } +// // Set the lockQC and other pre-requist properties by block 16 +// err = engineV2.ProposedBlockHandler(blockchain, currentBlock.Header()) +// if err != nil { +// t.Fatal("Error while handling block 16", err) +// } +// // Now try with block 17 but from a different chain +// err = engineV2.ProposedBlockHandler(blockchain, forkedBlock.Header()) +// if err != nil { +// t.Fatal("Fail propose proposedBlock handler", err) +// } +// // Should not receive anything from the channel +// select { +// case <-engineV2.BroadcastCh: +// t.Fatal("Should not trigger vote") +// case <-time.After(5 * time.Second): +// // Shoud not trigger setNewRound +// round, _, _, _ := engineV2.GetProperties() +// assert.Equal(t, utils.Round(8), round) +// } +// } diff --git a/consensus/tests/test_helper.go b/consensus/tests/test_helper.go index 5a134cc5d9..0fb4104826 100644 --- a/consensus/tests/test_helper.go +++ b/consensus/tests/test_helper.go @@ -266,7 +266,7 @@ func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params return blockchain, backend, currentBlock, signer } -func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig) (*BlockChain, *backends.SimulatedBackend, *types.Block, common.Address) { +func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig, numOfForkedBlocks int) (*BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, *types.Block) { // Preparation var err error backend := getCommonBackend(t, chainConfig) @@ -282,16 +282,58 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon currentBlock := blockchain.Genesis() + var currentForkBlock *types.Block + if numOfForkedBlocks != 0 { + currentForkBlock = blockchain.Genesis() + } + // Insert initial blocks for i := 1; i <= numOfBlocks; i++ { blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i) - merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" - // Build engine v2 compatible extra data field + header := createBlock(chainConfig, currentBlock, i, blockCoinBase, signer, signFn) + + block, err := insertBlock(blockchain, header) + if err != nil { + t.Fatal(err) + } + currentBlock = block + + } + if numOfForkedBlocks != 0 { + for i := 1; i <= numOfForkedBlocks; i++ { + forkedBlockCoinBase := fmt.Sprintf("0x222000000000000000000000000000000%03d", i) + + forkedBlockHeader := createBlock(chainConfig, currentForkBlock, i, forkedBlockCoinBase, signer, signFn) + + forkedBlock, err := insertBlock(blockchain, forkedBlockHeader) + if err != nil { + t.Fatal(err) + } + currentForkBlock = forkedBlock + } + } + // Update Signer as there is no previous signer assigned + err = UpdateSigner(blockchain) + if err != nil { + t.Fatal(err) + } + + return blockchain, backend, currentBlock, signer, currentForkBlock +} + +func createBlock(chainConfig *params.ChainConfig, startingBlock *types.Block, blockNumIteration int, blockCoinBase string, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error)) *types.Header { + currentBlock := startingBlock + merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" + var header *types.Header + // Build engine v2 compatible extra data field + if big.NewInt(int64(blockNumIteration)).Cmp(chainConfig.XDPoS.XDPoSV2Block) == 1 { + roundNumber := int64(blockNumIteration) - chainConfig.XDPoS.XDPoSV2Block.Int64() + proposedBlockInfo := &utils.BlockInfo{ Hash: currentBlock.Hash(), - Round: utils.Round(i - 1), - Number: big.NewInt(int64(i - 1)), + Round: utils.Round(roundNumber - 1), + Number: big.NewInt(int64(blockNumIteration - 1)), } // Genrate QC signedHash, err := signFn(accounts.Account{Address: signer}, utils.VoteSigHash(proposedBlockInfo).Bytes()) @@ -306,35 +348,31 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon } extra := utils.ExtraFields_v2{ - Round: utils.Round(i), + Round: utils.Round(roundNumber), QuorumCert: quorumCert, } extraInBytes, err := extra.EncodeToBytes() if err != nil { panic(fmt.Errorf("Error encode extra into bytes: %v", err)) } - - header := &types.Header{ + header = &types.Header{ Root: common.HexToHash(merkleRoot), - Number: big.NewInt(int64(i)), + Number: big.NewInt(int64(blockNumIteration)), ParentHash: currentBlock.Hash(), Coinbase: common.HexToAddress(blockCoinBase), Extra: extraInBytes, Validator: signedHash, } - block, err := insertBlock(blockchain, header) - if err != nil { - t.Fatal(err) + } else { + // V1 block + header = &types.Header{ + Root: common.HexToHash(merkleRoot), + Number: big.NewInt(int64(blockNumIteration)), + ParentHash: currentBlock.Hash(), + Coinbase: common.HexToAddress(blockCoinBase), } - currentBlock = block } - // Update Signer as there is no previous signer assigned - err = UpdateSigner(blockchain) - if err != nil { - t.Fatal(err) - } - - return blockchain, backend, currentBlock, signer + return header } func generateSignature(backend *backends.SimulatedBackend, header *types.Header) error { diff --git a/consensus/tests/timeout_test.go b/consensus/tests/timeout_test.go index 8041803197..b92aa62166 100644 --- a/consensus/tests/timeout_test.go +++ b/consensus/tests/timeout_test.go @@ -11,7 +11,7 @@ import ( // Timeout handler func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) { - blockchain, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine) + blockchain, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine, 0) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Set round to 1 @@ -24,7 +24,7 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) { err := engineV2.TimeoutHandler(timeoutMsg) assert.Nil(t, err) - currentRound, _, _ := engineV2.GetProperties() + currentRound, _, _, _ := engineV2.GetProperties() assert.Equal(t, utils.Round(1), currentRound) timeoutMsg = &utils.Timeout{ Round: utils.Round(1), @@ -32,7 +32,7 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) { } err = engineV2.TimeoutHandler(timeoutMsg) assert.Nil(t, err) - currentRound, _, _ = engineV2.GetProperties() + currentRound, _, _, _ = engineV2.GetProperties() assert.Equal(t, utils.Round(1), currentRound) // Create a timeout message that should trigger timeout pool hook timeoutMsg = &utils.Timeout{ @@ -45,7 +45,7 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) { syncInfoMsg := <-engineV2.BroadcastCh - currentRound, _, _ = engineV2.GetProperties() + currentRound, _, _, _ = engineV2.GetProperties() assert.NotNil(t, syncInfoMsg) @@ -62,7 +62,7 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) { } func TestThrowErrorIfTimeoutMsgRoundNotEqualToCurrentRound(t *testing.T) { - blockchain, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine) + blockchain, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine, 0) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Set round to 3 diff --git a/consensus/tests/vote_test.go b/consensus/tests/vote_test.go index 857e264695..13ebeedc48 100644 --- a/consensus/tests/vote_test.go +++ b/consensus/tests/vote_test.go @@ -12,18 +12,18 @@ import ( ) // VoteHandler -func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) { - blockchain, _, currentBlock, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine) +func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQCForFistV2Round(t *testing.T) { + blockchain, _, currentBlock, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine, 0) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 blockInfo := &utils.BlockInfo{ Hash: currentBlock.Hash(), - Round: utils.Round(11), + Round: utils.Round(1), Number: big.NewInt(11), } - // Set round to 11 - engineV2.SetNewRoundFaker(utils.Round(11), false) + // Set round to 5 + engineV2.SetNewRoundFaker(utils.Round(1), false) // Create two timeout message which will not reach vote pool threshold voteMsg := &utils.Vote{ ProposedBlockInfo: blockInfo, @@ -32,25 +32,25 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) { err := engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) - currentRound, lockQuorumCert, highestQuorumCert := engineV2.GetProperties() + currentRound, lockQuorumCert, highestQuorumCert, _ := engineV2.GetProperties() // Inilised with nil and 0 round assert.Nil(t, lockQuorumCert) assert.Nil(t, highestQuorumCert) - assert.Equal(t, utils.Round(11), currentRound) + assert.Equal(t, utils.Round(1), currentRound) voteMsg = &utils.Vote{ ProposedBlockInfo: blockInfo, Signature: []byte{2}, } err = engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) - currentRound, lockQuorumCert, highestQuorumCert = engineV2.GetProperties() + currentRound, lockQuorumCert, highestQuorumCert, _ = engineV2.GetProperties() // Still using the initlised value because we did not yet go to the next round assert.Nil(t, lockQuorumCert) assert.Nil(t, highestQuorumCert) - assert.Equal(t, utils.Round(11), currentRound) + assert.Equal(t, utils.Round(1), currentRound) - // Create a vote message that should trigger vote pool hook and increment the round to 12 + // Create a vote message that should trigger vote pool hook and increment the round to 6 voteMsg = &utils.Vote{ ProposedBlockInfo: blockInfo, Signature: []byte{3}, @@ -58,27 +58,82 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) { err = engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) - currentRound, lockQuorumCert, highestQuorumCert = engineV2.GetProperties() + currentRound, lockQuorumCert, highestQuorumCert, _ = engineV2.GetProperties() // The lockQC shall be the parent's QC round number - assert.Equal(t, utils.Round(10), lockQuorumCert.ProposedBlockInfo.Round) + assert.Equal(t, utils.Round(0), lockQuorumCert.ProposedBlockInfo.Round) // The highestQC proposedBlockInfo shall be the same as the one from its votes assert.Equal(t, highestQuorumCert.ProposedBlockInfo, voteMsg.ProposedBlockInfo) - // Check round has now changed from 11 to 12 - assert.Equal(t, utils.Round(12), currentRound) + // Check round has now changed from 5 to 6 + assert.Equal(t, utils.Round(2), currentRound) +} + +func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) { + blockchain, _, currentBlock, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 15, params.TestXDPoSMockChainConfigWithV2Engine, 0) + engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 + + blockInfo := &utils.BlockInfo{ + Hash: currentBlock.Hash(), + Round: utils.Round(5), + Number: big.NewInt(15), + } + + // Set round to 5 + engineV2.SetNewRoundFaker(utils.Round(5), false) + // Create two timeout message which will not reach vote pool threshold + voteMsg := &utils.Vote{ + ProposedBlockInfo: blockInfo, + Signature: []byte{1}, + } + + err := engineV2.VoteHandler(blockchain, voteMsg) + assert.Nil(t, err) + currentRound, lockQuorumCert, highestQuorumCert, _ := engineV2.GetProperties() + // Inilised with nil and 0 round + assert.Nil(t, lockQuorumCert) + assert.Nil(t, highestQuorumCert) + assert.Equal(t, utils.Round(5), currentRound) + voteMsg = &utils.Vote{ + ProposedBlockInfo: blockInfo, + Signature: []byte{2}, + } + err = engineV2.VoteHandler(blockchain, voteMsg) + assert.Nil(t, err) + currentRound, lockQuorumCert, highestQuorumCert, _ = engineV2.GetProperties() + // Still using the initlised value because we did not yet go to the next round + assert.Nil(t, lockQuorumCert) + assert.Nil(t, highestQuorumCert) + + assert.Equal(t, utils.Round(5), currentRound) + + // Create a vote message that should trigger vote pool hook and increment the round to 6 + voteMsg = &utils.Vote{ + ProposedBlockInfo: blockInfo, + Signature: []byte{3}, + } + + err = engineV2.VoteHandler(blockchain, voteMsg) + assert.Nil(t, err) + currentRound, lockQuorumCert, highestQuorumCert, _ = engineV2.GetProperties() + // The lockQC shall be the parent's QC round number + assert.Equal(t, utils.Round(4), lockQuorumCert.ProposedBlockInfo.Round) + // The highestQC proposedBlockInfo shall be the same as the one from its votes + assert.Equal(t, highestQuorumCert.ProposedBlockInfo, voteMsg.ProposedBlockInfo) + // Check round has now changed from 5 to 6 + assert.Equal(t, utils.Round(6), currentRound) } func TestThrowErrorIfVoteMsgRoundNotEqualToCurrentRound(t *testing.T) { - blockchain, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine) + blockchain, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 15, params.TestXDPoSMockChainConfigWithV2Engine, 0) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 blockInfo := &utils.BlockInfo{ Hash: common.HexToHash("0x1"), - Round: utils.Round(12), + Round: utils.Round(6), Number: big.NewInt(999), } - // Set round to 13 - engineV2.SetNewRoundFaker(utils.Round(13), false) + // Set round to 7 + engineV2.SetNewRoundFaker(utils.Round(7), false) voteMsg := &utils.Vote{ ProposedBlockInfo: blockInfo, Signature: []byte{1}, @@ -87,27 +142,27 @@ func TestThrowErrorIfVoteMsgRoundNotEqualToCurrentRound(t *testing.T) { // voteRound > currentRound err := engineV2.VoteHandler(blockchain, voteMsg) assert.NotNil(t, err) - assert.Equal(t, "Vote message round number: 12 does not match currentRound: 13", err.Error()) + assert.Equal(t, "Vote message round number: 6 does not match currentRound: 7", err.Error()) - // Set round to 11 - engineV2.SetNewRoundFaker(utils.Round(11), false) + // Set round to 5 + engineV2.SetNewRoundFaker(utils.Round(5), false) err = engineV2.VoteHandler(blockchain, voteMsg) assert.NotNil(t, err) // voteRound < currentRound - assert.Equal(t, "Vote message round number: 12 does not match currentRound: 11", err.Error()) + assert.Equal(t, "Vote message round number: 6 does not match currentRound: 5", err.Error()) } func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { - blockchain, _, currentBlock, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine) + blockchain, _, currentBlock, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 15, params.TestXDPoSMockChainConfigWithV2Engine, 0) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 - // Set round to 1 - engineV2.SetNewRoundFaker(utils.Round(11), false) + // Set round to 5 + engineV2.SetNewRoundFaker(utils.Round(5), false) // Start with vote messages blockInfo := &utils.BlockInfo{ Hash: currentBlock.Hash(), - Round: utils.Round(11), + Round: utils.Round(5), Number: big.NewInt(11), } // Create two vote message which will not reach vote pool threshold @@ -118,20 +173,20 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { err := engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) - currentRound, lockQuorumCert, highestQuorumCert := engineV2.GetProperties() + currentRound, lockQuorumCert, highestQuorumCert, _ := engineV2.GetProperties() // Inilised with nil and 0 round assert.Nil(t, lockQuorumCert) assert.Nil(t, highestQuorumCert) - assert.Equal(t, utils.Round(11), currentRound) + assert.Equal(t, utils.Round(5), currentRound) voteMsg = &utils.Vote{ ProposedBlockInfo: blockInfo, Signature: []byte{2}, } err = engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) - currentRound, _, _ = engineV2.GetProperties() - assert.Equal(t, utils.Round(11), currentRound) + currentRound, _, _, _ = engineV2.GetProperties() + assert.Equal(t, utils.Round(5), currentRound) // Create a vote message that should trigger vote pool hook voteMsg = &utils.Vote{ @@ -141,49 +196,49 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { err = engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) - // Check round has now changed from 11 to 12 - currentRound, lockQuorumCert, highestQuorumCert = engineV2.GetProperties() + // Check round has now changed from 5 to 6 + currentRound, lockQuorumCert, highestQuorumCert, _ = engineV2.GetProperties() // The lockQC shall be the parent's QC round number - assert.Equal(t, utils.Round(10), lockQuorumCert.ProposedBlockInfo.Round) + assert.Equal(t, utils.Round(4), lockQuorumCert.ProposedBlockInfo.Round) // The highestQC proposedBlockInfo shall be the same as the one from its votes assert.Equal(t, highestQuorumCert.ProposedBlockInfo, voteMsg.ProposedBlockInfo) - assert.Equal(t, utils.Round(12), currentRound) + assert.Equal(t, utils.Round(6), currentRound) // We shall have highestQuorumCert in engine now, let's do timeout msg to see if we can broadcast SyncInfo which contains both highestQuorumCert and HighestTimeoutCert // First, all incoming old timeout msg shall not be processed timeoutMsg := &utils.Timeout{ - Round: utils.Round(11), + Round: utils.Round(5), Signature: []byte{1}, } err = engineV2.TimeoutHandler(timeoutMsg) assert.NotNil(t, err) - assert.Equal(t, "Timeout message round number: 11 does not match currentRound: 12", err.Error()) + assert.Equal(t, "Timeout message round number: 5 does not match currentRound: 6", err.Error()) // Ok, let's do the timeout msg which is on the same round as the current round by creating two timeout message which will not reach timeout pool threshold timeoutMsg = &utils.Timeout{ - Round: utils.Round(12), + Round: utils.Round(6), Signature: []byte{1}, } err = engineV2.TimeoutHandler(timeoutMsg) assert.Nil(t, err) - currentRound, _, _ = engineV2.GetProperties() - assert.Equal(t, utils.Round(12), currentRound) + currentRound, _, _, _ = engineV2.GetProperties() + assert.Equal(t, utils.Round(6), currentRound) timeoutMsg = &utils.Timeout{ - Round: utils.Round(12), + Round: utils.Round(6), Signature: []byte{2}, } err = engineV2.TimeoutHandler(timeoutMsg) assert.Nil(t, err) - currentRound, _, _ = engineV2.GetProperties() - assert.Equal(t, utils.Round(12), currentRound) + currentRound, _, _, _ = engineV2.GetProperties() + assert.Equal(t, utils.Round(6), currentRound) // Create a timeout message that should trigger timeout pool hook timeoutMsg = &utils.Timeout{ - Round: utils.Round(12), + Round: utils.Round(6), Signature: []byte{3}, } @@ -196,14 +251,14 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { // Should have HighestQuorumCert from previous round votes qc := syncInfoMsg.(*utils.SyncInfo).HighestQuorumCert assert.NotNil(t, qc) - assert.Equal(t, utils.Round(11), qc.ProposedBlockInfo.Round) + assert.Equal(t, utils.Round(5), qc.ProposedBlockInfo.Round) tc := syncInfoMsg.(*utils.SyncInfo).HighestTimeoutCert assert.NotNil(t, tc) - assert.Equal(t, utils.Round(12), tc.Round) + assert.Equal(t, utils.Round(6), tc.Round) sigatures := []utils.Signature{[]byte{1}, []byte{2}, []byte{3}} assert.ElementsMatch(t, tc.Signatures, sigatures) // Round shall be +1 now - currentRound, _, _ = engineV2.GetProperties() - assert.Equal(t, utils.Round(13), currentRound) + currentRound, _, _, _ = engineV2.GetProperties() + assert.Equal(t, utils.Round(7), currentRound) }