From 5f9fd80733675878b8059210930d7886401bbb62 Mon Sep 17 00:00:00 2001 From: Jianrong Date: Sun, 12 Dec 2021 20:18:15 +1100 Subject: [PATCH] add more propose block handler test --- consensus/XDPoS/engines/engine_v2/engine.go | 75 ++++++++++-------- consensus/tests/proposed_block_test.go | 88 ++++++++++++++------- consensus/tests/sync_info_test.go | 31 ++++++++ consensus/tests/test_helper.go | 38 +++++---- consensus/tests/vote_test.go | 6 +- 5 files changed, 158 insertions(+), 80 deletions(-) create mode 100644 consensus/tests/sync_info_test.go diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index 98288c2d30..983f8a5007 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -74,9 +74,20 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 { timeoutPool: timeoutPool, votePool: votePool, - highestTimeoutCert: nil, - highestQuorumCert: nil, + highestTimeoutCert: &utils.TimeoutCert{ + Round: utils.Round(0), + Signatures: []utils.Signature{}, + }, + highestQuorumCert: &utils.QuorumCert{ + ProposedBlockInfo: &utils.BlockInfo{ + Hash: common.Hash{}, + Round: utils.Round(0), + Number: big.NewInt(0), + }, + Signatures: []utils.Signature{}, + }, highestVotedRound: utils.Round(0), + highestCommitBlock: nil, } // Add callback to the timer timer.OnTimeoutFn = engine.onCountdownTimeout @@ -436,13 +447,6 @@ func (x *XDPoS_v2) VerifyHeader(chain consensus.ChainReader, header *types.Heade return nil } -// Utils for test to check currentRound value -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, x.highestVotedRound -} - /* SyncInfo workflow */ @@ -704,7 +708,7 @@ func (x *XDPoS_v2) verifyTC(timeoutCert *utils.TimeoutCert) error { func (x *XDPoS_v2) processQC(blockChainReader consensus.ChainReader, quorumCert *utils.QuorumCert) error { log.Trace("[ProcessQC][Before]", "HighQC", x.highestQuorumCert) // 1. Update HighestQC - if x.highestQuorumCert == nil || (quorumCert.ProposedBlockInfo.Round > x.highestQuorumCert.ProposedBlockInfo.Round) { + if quorumCert.ProposedBlockInfo.Round > x.highestQuorumCert.ProposedBlockInfo.Round { x.highestQuorumCert = quorumCert } // 2. Get QC from header and update lockQuorumCert(lockQuorumCert is the parent of highestQC) @@ -745,7 +749,7 @@ func (x *XDPoS_v2) processQC(blockChainReader consensus.ChainReader, quorumCert 2. Check TC round >= node's currentRound. If yes, call setNewRound */ func (x *XDPoS_v2) processTC(timeoutCert *utils.TimeoutCert) error { - if x.highestTimeoutCert == nil || timeoutCert.Round > x.highestTimeoutCert.Round { + if timeoutCert.Round > x.highestTimeoutCert.Round { x.highestTimeoutCert = timeoutCert } if timeoutCert.Round >= x.currentRound { @@ -898,14 +902,6 @@ func (x *XDPoS_v2) getCurrentRoundMasterNodes() []common.Address { return []common.Address{} } -/* - Testing tools -*/ - -func (x *XDPoS_v2) SetHighestQuorumCert(qc *utils.QuorumCert) { - x.highestQuorumCert = qc -} - func (x *XDPoS_v2) getSyncInfo() *utils.SyncInfo { return &utils.SyncInfo{ HighestQuorumCert: x.highestQuorumCert, @@ -913,21 +909,6 @@ func (x *XDPoS_v2) getSyncInfo() *utils.SyncInfo { } } -func (x *XDPoS_v2) SetNewRoundFaker(newRound utils.Round, resetTimer bool) { - x.lock.Lock() - defer x.lock.Unlock() - // Reset a bunch of things - if resetTimer { - x.timeoutWorker.Reset() - } - x.currentRound = newRound -} - -// Utils for test to check currentRound value -func (x *XDPoS_v2) GetCurrentRound() utils.Round { - return x.currentRound -} - //TODO: find parent and grandparent and grandgrandparent block, check round number, if so, commit grandgrandparent func (x *XDPoS_v2) commitBlocks(blockCahinReader consensus.ChainReader, proposedBlockHeader *types.Header, proposedBlockRound *utils.Round) (bool, error) { // XDPoS v1.0 switch to v2.0, skip commit @@ -978,3 +959,29 @@ func (x *XDPoS_v2) isExtendingFromAncestor(blockChainReader consensus.ChainReade } return false, nil } + +/* + Testing tools +*/ + +func (x *XDPoS_v2) SetNewRoundFaker(newRound utils.Round, resetTimer bool) { + x.lock.Lock() + defer x.lock.Unlock() + // Reset a bunch of things + if resetTimer { + x.timeoutWorker.Reset() + } + x.currentRound = newRound +} + +// Utils for test to check currentRound value +func (x *XDPoS_v2) GetCurrentRound() utils.Round { + return x.currentRound +} + +// Utils for test to check currentRound value +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, x.highestVotedRound +} diff --git a/consensus/tests/proposed_block_test.go b/consensus/tests/proposed_block_test.go index 6ec376fb2f..ff87d76960 100644 --- a/consensus/tests/proposed_block_test.go +++ b/consensus/tests/proposed_block_test.go @@ -157,33 +157,63 @@ func TestShouldNotSendVoteMsgIfBlockInfoRoundNotEqualCurrentRound(t *testing.T) } } -// 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 +/* + Block and round relationship diagram for this test + ... - 13(3) - 14(4) - 15(5) - 16(6) + \ 14'(7) +*/ +func TestShouldNotSendVoteMsgIfBlockNotExtendedFromAncestor(t *testing.T) { + // Block number 15, 16 have forks and forkedBlock is the 16th + blockchain, _, currentBlock, _, forkedBlock := PrepareXDCTestBlockChainForV2Engine(t, 16, params.TestXDPoSMockChainConfigWithV2Engine, 3) + 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) -// } -// } + var extraField utils.ExtraFields_v2 + err := utils.DecodeBytesExtraFields(forkedBlock.Extra(), &extraField) + if err != nil { + t.Fatal("Fail to decode extra data", err) + } + assert.Equal(t, utils.Round(9), extraField.Round) + // 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) + } + vote := <-engineV2.BroadcastCh + assert.Equal(t, utils.Round(6), vote.(*utils.Vote).ProposedBlockInfo.Round) + + // Find the first forked block at block 14th + firstForkedBlock := blockchain.GetBlockByHash(blockchain.GetBlockByHash(forkedBlock.ParentHash()).ParentHash()) + engineV2.SetNewRoundFaker(utils.Round(7), false) + err = engineV2.ProposedBlockHandler(blockchain, firstForkedBlock.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(7), round) + } +} + +func TestShouldSendVoteMsg(t *testing.T) { + // Block number 15, 16 have forks and forkedBlock is the 16th + blockchain, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 13, params.TestXDPoSMockChainConfigWithV2Engine, 0) + engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 + + // Block 11 is first v2 block + for i := 11; i < 14; i++ { + blockHeader := blockchain.GetBlockByNumber(uint64(i)).Header() + err := engineV2.ProposedBlockHandler(blockchain, blockHeader) + if err != nil { + t.Fatal(err) + } + round, _, _, _ := engineV2.GetProperties() + assert.Equal(t, utils.Round(i-10), round) + vote := <-engineV2.BroadcastCh + assert.Equal(t, round, vote.(*utils.Vote).ProposedBlockInfo.Round) + } +} diff --git a/consensus/tests/sync_info_test.go b/consensus/tests/sync_info_test.go new file mode 100644 index 0000000000..f9cb2cb8fa --- /dev/null +++ b/consensus/tests/sync_info_test.go @@ -0,0 +1,31 @@ +package tests + +import ( + "testing" + + "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" + "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" + "github.com/XinFinOrg/XDPoSChain/params" +) + +func TestSyncInfoForFirstV2BlockMsgWithoutQC(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 + + var extraField utils.ExtraFields_v2 + err := utils.DecodeBytesExtraFields(currentBlock.Extra(), &extraField) + if err != nil { + t.Fatal("Fail to decode extra data", err) + } + + syncInfoMsg := &utils.SyncInfo{ + HighestQuorumCert: extraField.QuorumCert, + HighestTimeoutCert: nil, // Initial value? + } + + err = engineV2.SyncInfoHandler(blockchain, syncInfoMsg) + if err != nil { + t.Fatal(err) + } +} diff --git a/consensus/tests/test_helper.go b/consensus/tests/test_helper.go index 0fb4104826..688a48011f 100644 --- a/consensus/tests/test_helper.go +++ b/consensus/tests/test_helper.go @@ -283,28 +283,28 @@ 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) - - header := createBlock(chainConfig, currentBlock, i, blockCoinBase, signer, signFn) + roundNumber := int64(i) - chainConfig.XDPoS.XDPoSV2Block.Int64() + header := createBlock(chainConfig, currentBlock, i, roundNumber, 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++ { + // Produce forked block for the last numOfForkedBlocks'th blocks + if numOfForkedBlocks != 0 && i > numOfBlocks-numOfForkedBlocks { + if currentForkBlock == nil { + currentForkBlock = currentBlock + } forkedBlockCoinBase := fmt.Sprintf("0x222000000000000000000000000000000%03d", i) - forkedBlockHeader := createBlock(chainConfig, currentForkBlock, i, forkedBlockCoinBase, signer, signFn) + forkedBlockRoundNumber := roundNumber + int64(numOfForkedBlocks) + + forkedBlockHeader := createBlock(chainConfig, currentForkBlock, i, forkedBlockRoundNumber, forkedBlockCoinBase, signer, signFn) forkedBlock, err := insertBlock(blockchain, forkedBlockHeader) if err != nil { @@ -312,7 +312,9 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon } currentForkBlock = forkedBlock } + currentBlock = block } + // Update Signer as there is no previous signer assigned err = UpdateSigner(blockchain) if err != nil { @@ -322,18 +324,26 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon 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 { +func createBlock(chainConfig *params.ChainConfig, startingBlock *types.Block, blockNumIteration int, roundNumber int64, 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() + + var extraField utils.ExtraFields_v2 + var round utils.Round + err := utils.DecodeBytesExtraFields(currentBlock.Extra(), &extraField) + if err != nil { + round = utils.Round(0) + } else { + round = extraField.Round + } proposedBlockInfo := &utils.BlockInfo{ Hash: currentBlock.Hash(), - Round: utils.Round(roundNumber - 1), - Number: big.NewInt(int64(blockNumIteration - 1)), + Round: round, + Number: currentBlock.Number(), } // Genrate QC signedHash, err := signFn(accounts.Account{Address: signer}, utils.VoteSigHash(proposedBlockInfo).Bytes()) diff --git a/consensus/tests/vote_test.go b/consensus/tests/vote_test.go index 13ebeedc48..785712a0d3 100644 --- a/consensus/tests/vote_test.go +++ b/consensus/tests/vote_test.go @@ -33,7 +33,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQCForFistV2Round(t *te err := engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) currentRound, lockQuorumCert, highestQuorumCert, _ := engineV2.GetProperties() - // Inilised with nil and 0 round + // initialised with nil and 0 round assert.Nil(t, lockQuorumCert) assert.Nil(t, highestQuorumCert) assert.Equal(t, utils.Round(1), currentRound) @@ -88,7 +88,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) { err := engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) currentRound, lockQuorumCert, highestQuorumCert, _ := engineV2.GetProperties() - // Inilised with nil and 0 round + // initialised with nil and 0 round assert.Nil(t, lockQuorumCert) assert.Nil(t, highestQuorumCert) assert.Equal(t, utils.Round(5), currentRound) @@ -174,7 +174,7 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { err := engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) currentRound, lockQuorumCert, highestQuorumCert, _ := engineV2.GetProperties() - // Inilised with nil and 0 round + // initialised with nil and 0 round assert.Nil(t, lockQuorumCert) assert.Nil(t, highestQuorumCert)