From 15275ded651f13683b865247d5f8394ba010a42f Mon Sep 17 00:00:00 2001 From: Jianrong Date: Thu, 30 Dec 2021 21:05:00 +1100 Subject: [PATCH] Commit grand grand parent block(continous rounds) if enough votes or by proposedBlockHandler --- consensus/XDPoS/engines/engine_v2/engine.go | 52 ++++-- consensus/tests/adaptor_test.go | 2 +- consensus/tests/countdown_test.go | 2 +- consensus/tests/proposed_block_test.go | 176 ++++++++++++++++++-- consensus/tests/sync_info_test.go | 11 +- consensus/tests/test_helper.go | 4 +- consensus/tests/timeout_test.go | 10 +- consensus/tests/vote_test.go | 35 ++-- eth/bft/bft_handler.go | 10 +- 9 files changed, 238 insertions(+), 64 deletions(-) diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index 05abab71fa..993f872542 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -831,6 +831,7 @@ func (x *XDPoS_v2) sendVote(chainReader consensus.ChainReader, blockInfo *utils. signedHash, err := x.signSignature(utils.VoteSigHash(blockInfo)) if err != nil { + log.Error("signSignature when sending out Vote", "BlockInfoHash", blockInfo.Hash, "Error", err) return err } @@ -840,7 +841,11 @@ func (x *XDPoS_v2) sendVote(chainReader consensus.ChainReader, blockInfo *utils. Signature: signedHash, } - x.voteHandler(chainReader, voteMsg) + err = x.voteHandler(chainReader, voteMsg) + if err != nil { + log.Error("sendVote error", "BlockInfoHash", blockInfo.Hash, "Error", err) + return err + } x.broadcastToBftChannel(voteMsg) return nil } @@ -854,6 +859,7 @@ func (x *XDPoS_v2) sendVote(chainReader consensus.ChainReader, blockInfo *utils. func (x *XDPoS_v2) sendTimeout() error { signedHash, err := x.signSignature(utils.TimeoutSigHash(&x.currentRound)) if err != nil { + log.Error("signSignature when sending out TC", "Error", err) return err } timeoutMsg := &utils.Timeout{ @@ -861,7 +867,11 @@ func (x *XDPoS_v2) sendTimeout() error { Signature: signedHash, } - x.timeoutHandler(timeoutMsg) + err = x.timeoutHandler(timeoutMsg) + if err != nil { + log.Error("TimeoutHandler error", "TimeoutRound", timeoutMsg.Round, "Error", err) + return err + } x.broadcastToBftChannel(timeoutMsg) return nil } @@ -930,36 +940,49 @@ func (x *XDPoS_v2) getSyncInfo() *utils.SyncInfo { } } -//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) { +//Find parent and grandparent, check round number, if so, commit grandparent(grandGrandParent of currentBlock) +func (x *XDPoS_v2) commitBlocks(blockChainReader consensus.ChainReader, proposedBlockHeader *types.Header, proposedBlockRound *utils.Round) (bool, error) { // XDPoS v1.0 switch to v2.0, skip commit if big.NewInt(0).Sub(proposedBlockHeader.Number, big.NewInt(2)).Cmp(x.config.XDPoSV2Block) <= 0 { return false, nil } - // Find the last two parent block and check their rounds are the continous - parentBlock := blockCahinReader.GetHeaderByHash(proposedBlockHeader.ParentHash) + // Find the last two parent block and check their rounds are the continuous + parentBlock := blockChainReader.GetHeaderByHash(proposedBlockHeader.ParentHash) var decodedExtraField utils.ExtraFields_v2 err := utils.DecodeBytesExtraFields(parentBlock.Extra, &decodedExtraField) if err != nil { + log.Error("Fail to execute first DecodeBytesExtraFields for commiting block", "ProposedBlockHash", proposedBlockHeader.Hash()) return false, err } if *proposedBlockRound-1 != decodedExtraField.Round { + log.Debug("[commitBlocks] Rounds not continuous(parent) found when committing block", "proposedBlockRound", proposedBlockRound, "decodedExtraField.Round", decodedExtraField.Round, "proposedBlockHeaderHash", proposedBlockHeader.Hash()) return false, nil } - // If parent round is continous, we check grandparent - grandParentBlock := blockCahinReader.GetHeaderByHash(parentBlock.ParentHash) + // If parent round is continuous, we check grandparent + grandParentBlock := blockChainReader.GetHeaderByHash(parentBlock.ParentHash) err = utils.DecodeBytesExtraFields(grandParentBlock.Extra, &decodedExtraField) if err != nil { + log.Error("Fail to execute second DecodeBytesExtraFields for commiting block", "parentBlockHash", parentBlock.Hash()) return false, err } if *proposedBlockRound-2 != decodedExtraField.Round { + log.Debug("[commitBlocks] Rounds not continuous(grand parent) found when committing block", "proposedBlockRound", proposedBlockRound, "decodedExtraField.Round", decodedExtraField.Round, "proposedBlockHeaderHash", proposedBlockHeader.Hash()) return false, nil } - // TODO: Commit the grandParent block - - return true, nil + // Commit the grandParent block + if x.highestCommitBlock == nil || (x.highestCommitBlock.Round < decodedExtraField.Round && x.highestCommitBlock.Number.Cmp(grandParentBlock.Number) == -1) { + x.highestCommitBlock = &utils.BlockInfo{ + Number: grandParentBlock.Number, + Hash: grandParentBlock.Hash(), + Round: decodedExtraField.Round, + } + log.Debug("👴 Successfully committed block", "Committed block Hash", x.highestCommitBlock.Hash, "Committed round", x.highestCommitBlock.Round) + return true, nil + } + // Everything else, fail to commit + return false, nil } func (x *XDPoS_v2) isExtendingFromAncestor(blockChainReader consensus.ChainReader, currentBlock *utils.BlockInfo, ancestorBlock *utils.BlockInfo) (bool, error) { @@ -969,10 +992,11 @@ func (x *XDPoS_v2) isExtendingFromAncestor(blockChainReader consensus.ChainReade for i := 0; i < blockNumDiff; i++ { parentBlock := blockChainReader.GetHeaderByHash(nextBlockHash) if parentBlock == nil { - return false, fmt.Errorf("Could not find its parent block when checking whether currentBlock %v is extending from the ancestorBlock %v", currentBlock.Number, ancestorBlock.Number) + return false, fmt.Errorf("Could not find its parent block when checking whether currentBlock %v with hash %v is extending from the ancestorBlock %v", currentBlock.Number, currentBlock.Hash, ancestorBlock.Number) } else { nextBlockHash = parentBlock.ParentHash } + log.Debug("[isExtendingFromAncestor] Found parent block", "CurrentBlockHash", currentBlock.Hash, "ParentHash", nextBlockHash) } if nextBlockHash == ancestorBlock.Hash { @@ -1001,8 +1025,8 @@ func (x *XDPoS_v2) GetCurrentRound() utils.Round { } // Utils for test to check currentRound value -func (x *XDPoS_v2) GetProperties() (utils.Round, *utils.QuorumCert, *utils.QuorumCert, utils.Round) { +func (x *XDPoS_v2) GetProperties() (utils.Round, *utils.QuorumCert, *utils.QuorumCert, utils.Round, *utils.BlockInfo) { x.lock.Lock() defer x.lock.Unlock() - return x.currentRound, x.lockQuorumCert, x.highestQuorumCert, x.highestVotedRound + return x.currentRound, x.lockQuorumCert, x.highestQuorumCert, x.highestVotedRound, x.highestCommitBlock } diff --git a/consensus/tests/adaptor_test.go b/consensus/tests/adaptor_test.go index 6d27903818..f6ee182151 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, 0) + blockchain, backend, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 10, params.TestXDPoSMockChainConfigWithV2Engine, 0) adaptor := blockchain.Engine().(*XDPoS.XDPoS) addressFromAdaptor, errorAdaptor := adaptor.Author(currentBlock.Header()) diff --git a/consensus/tests/countdown_test.go b/consensus/tests/countdown_test.go index bd342f7808..3e52cd9593 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, 0) + 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 8e728a8e60..6aecbbd372 100644 --- a/consensus/tests/proposed_block_test.go +++ b/consensus/tests/proposed_block_test.go @@ -1,6 +1,7 @@ package tests import ( + "fmt" "testing" "time" @@ -10,9 +11,9 @@ import ( "github.com/stretchr/testify/assert" ) -func TestProcessFirstV2BlockAndSendVoteMsg(t *testing.T) { +func TestShouldSendVoteMsgAndCommitGrandGrandParentBlock(t *testing.T) { // Block 11 is the first v2 block with round of 1 - blockchain, _, currentBlock, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine, 0) + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine, 0) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 var extraField utils.ExtraFields_v2 @@ -33,16 +34,159 @@ func TestProcessFirstV2BlockAndSendVoteMsg(t *testing.T) { assert.NotNil(t, voteMsg) assert.Equal(t, currentBlock.Hash(), voteMsg.(*utils.Vote).ProposedBlockInfo.Hash) - round, _, highestQC, _ := engineV2.GetProperties() + round, _, highestQC, _, _ := engineV2.GetProperties() // Shoud trigger setNewRound assert.Equal(t, utils.Round(1), round) // Should not update the highestQC assert.Equal(t, utils.Round(0), highestQC.ProposedBlockInfo.Round) + // Insert another Block, but it won't trigger commit + blockNum := 12 + blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum) + blockHeader := createBlock(params.TestXDPoSMockChainConfigWithV2Engine, currentBlock, blockNum, 2, blockCoinBase, signer, signFn) + block12, err := insertBlock(blockchain, blockHeader) + if err != nil { + t.Fatal(err) + } + err = engineV2.ProposedBlockHandler(blockchain, block12.Header()) + if err != nil { + t.Fatal("Fail propose proposedBlock handler", err) + } + // Trigger send vote again but for a new round + voteMsg = <-engineV2.BroadcastCh + assert.NotNil(t, voteMsg) + round, _, highestQC, _, _ = engineV2.GetProperties() + // Shoud trigger setNewRound + assert.Equal(t, utils.Round(2), round) + assert.Equal(t, utils.Round(1), highestQC.ProposedBlockInfo.Round) + + // Insert one more Block, but still won't trigger commit + blockNum = 13 + blockCoinBase = fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum) + blockHeader = createBlock(params.TestXDPoSMockChainConfigWithV2Engine, block12, blockNum, 3, blockCoinBase, signer, signFn) + block13, err := insertBlock(blockchain, blockHeader) + if err != nil { + t.Fatal(err) + } + err = engineV2.ProposedBlockHandler(blockchain, block13.Header()) + if err != nil { + t.Fatal("Fail propose proposedBlock handler", err) + } + // Trigger send vote again but for a new round + voteMsg = <-engineV2.BroadcastCh + assert.NotNil(t, voteMsg) + round, _, highestQC, _, highestCommitBlock := engineV2.GetProperties() + // Shoud NOT trigger setNewRound as the new block parent QC is round 1 but the currentRound is already 2 + assert.Equal(t, utils.Round(3), round) + assert.Equal(t, utils.Round(2), highestQC.ProposedBlockInfo.Round) + assert.Nil(t, highestCommitBlock) + + // Insert one more Block, this time will trigger commit + blockNum = 14 + blockCoinBase = fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum) + blockHeader = createBlock(params.TestXDPoSMockChainConfigWithV2Engine, block13, blockNum, 4, blockCoinBase, signer, signFn) + block14, err := insertBlock(blockchain, blockHeader) + if err != nil { + t.Fatal(err) + } + err = engineV2.ProposedBlockHandler(blockchain, block14.Header()) + if err != nil { + t.Fatal("Fail propose proposedBlock handler", err) + } + // Trigger send vote again but for a new round + voteMsg = <-engineV2.BroadcastCh + assert.NotNil(t, voteMsg) + round, _, highestQC, _, highestCommitBlock = engineV2.GetProperties() + + assert.Equal(t, utils.Round(4), round) + assert.Equal(t, utils.Round(3), highestQC.ProposedBlockInfo.Round) + assert.Equal(t, currentBlock.Hash(), highestCommitBlock.Hash) + assert.Equal(t, currentBlock.Number(), highestCommitBlock.Number) + assert.Equal(t, utils.Round(1), highestCommitBlock.Round) +} + +func TestShouldNotCommitIfRoundsNotContinousFor3Rounds(t *testing.T) { + // Block 11 is the first v2 block with round of 1 + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 15, 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) + } + + 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, _, highestCommitBlock := engineV2.GetProperties() + + grandGrandParentBlock := blockchain.GetBlockByNumber(12) + // Shoud trigger setNewRound + assert.Equal(t, utils.Round(5), round) + assert.Equal(t, utils.Round(4), highestQC.ProposedBlockInfo.Round) + assert.Equal(t, grandGrandParentBlock.Hash(), highestCommitBlock.Hash) + assert.Equal(t, grandGrandParentBlock.Number(), highestCommitBlock.Number) + assert.Equal(t, utils.Round(2), highestCommitBlock.Round) + + // Injecting new block which have gaps in the round number (Round 7 instead of 6) + blockNum := 16 + blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum) + blockHeader := createBlock(params.TestXDPoSMockChainConfigWithV2Engine, currentBlock, blockNum, 7, blockCoinBase, signer, signFn) + block16, err := insertBlock(blockchain, blockHeader) + if err != nil { + t.Fatal(err) + } + err = engineV2.ProposedBlockHandler(blockchain, block16.Header()) + if err != nil { + t.Fatal("Fail propose proposedBlock handler", err) + } + // Trigger send vote again but for a new round + voteMsg = <-engineV2.BroadcastCh + assert.NotNil(t, voteMsg) + round, _, highestQC, _, highestCommitBlock = engineV2.GetProperties() + grandGrandParentBlock = blockchain.GetBlockByNumber(13) + + assert.Equal(t, utils.Round(6), round) + assert.Equal(t, utils.Round(5), highestQC.ProposedBlockInfo.Round) + // It commit its grandgrandparent block + assert.Equal(t, grandGrandParentBlock.Hash(), highestCommitBlock.Hash) + assert.Equal(t, grandGrandParentBlock.Number(), highestCommitBlock.Number) + assert.Equal(t, utils.Round(3), highestCommitBlock.Round) + + blockNum = 17 + blockCoinBase = fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum) + blockHeader = createBlock(params.TestXDPoSMockChainConfigWithV2Engine, block16, blockNum, 8, blockCoinBase, signer, signFn) + block17, err := insertBlock(blockchain, blockHeader) + if err != nil { + t.Fatal(err) + } + err = engineV2.ProposedBlockHandler(blockchain, block17.Header()) + if err != nil { + t.Fatal("Fail propose proposedBlock handler", err) + } + // Trigger send vote again but for a new round + voteMsg = <-engineV2.BroadcastCh + assert.NotNil(t, voteMsg) + round, _, highestQC, _, highestCommitBlock = engineV2.GetProperties() + + assert.Equal(t, utils.Round(8), round) + assert.Equal(t, utils.Round(7), highestQC.ProposedBlockInfo.Round) + // Should NOT commit, the `grandGrandParentBlock` is still on blockNum 13 + assert.Equal(t, grandGrandParentBlock.Hash(), highestCommitBlock.Hash) + assert.Equal(t, grandGrandParentBlock.Number(), highestCommitBlock.Number) + assert.Equal(t, utils.Round(3), highestCommitBlock.Round) + } func TestProposedBlockMessageHandlerSuccessfullyGenerateVote(t *testing.T) { - blockchain, _, currentBlock, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 16, params.TestXDPoSMockChainConfigWithV2Engine, 0) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 16, params.TestXDPoSMockChainConfigWithV2Engine, 0) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Set current round to 5 @@ -63,7 +207,7 @@ func TestProposedBlockMessageHandlerSuccessfullyGenerateVote(t *testing.T) { assert.NotNil(t, voteMsg) assert.Equal(t, currentBlock.Hash(), voteMsg.(*utils.Vote).ProposedBlockInfo.Hash) - round, _, highestQC, _ := engineV2.GetProperties() + round, _, highestQC, _, _ := engineV2.GetProperties() // Shoud trigger setNewRound assert.Equal(t, utils.Round(6), round) assert.Equal(t, extraField.QuorumCert.Signatures, highestQC.Signatures) @@ -72,7 +216,7 @@ func TestProposedBlockMessageHandlerSuccessfullyGenerateVote(t *testing.T) { // 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) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 16, params.TestXDPoSMockChainConfigWithV2Engine, 0) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Set current round to 6 @@ -89,14 +233,14 @@ func TestShouldNotSetNewRound(t *testing.T) { t.Fatal("Fail propose proposedBlock handler", err) } - round, _, highestQC, _ := engineV2.GetProperties() + 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) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 16, params.TestXDPoSMockChainConfigWithV2Engine, 0) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Set current round to 5 @@ -111,7 +255,7 @@ func TestShouldNotSendVoteMessageIfAlreadyVoteForThisRound(t *testing.T) { assert.NotNil(t, voteMsg) assert.Equal(t, currentBlock.Hash(), voteMsg.(*utils.Vote).ProposedBlockInfo.Hash) - round, _, _, highestVotedRound := engineV2.GetProperties() + round, _, _, highestVotedRound, _ := engineV2.GetProperties() // Shoud trigger setNewRound assert.Equal(t, utils.Round(6), round) assert.Equal(t, utils.Round(6), highestVotedRound) @@ -127,14 +271,14 @@ func TestShouldNotSendVoteMessageIfAlreadyVoteForThisRound(t *testing.T) { t.Fatal("Should not trigger vote") case <-time.After(5 * time.Second): // Shoud not trigger setNewRound - round, _, _, highestVotedRound = engineV2.GetProperties() + 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) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 16, params.TestXDPoSMockChainConfigWithV2Engine, 0) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Set current round to 8 @@ -156,7 +300,7 @@ func TestShouldNotSendVoteMsgIfBlockInfoRoundNotEqualCurrentRound(t *testing.T) t.Fatal("Should not trigger vote") case <-time.After(5 * time.Second): // Shoud not trigger setNewRound - round, _, _, _ := engineV2.GetProperties() + round, _, _, _, _ := engineV2.GetProperties() assert.Equal(t, utils.Round(8), round) } } @@ -168,7 +312,7 @@ func TestShouldNotSendVoteMsgIfBlockInfoRoundNotEqualCurrentRound(t *testing.T) */ 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) + blockchain, _, currentBlock, _, _, forkedBlock := PrepareXDCTestBlockChainForV2Engine(t, 16, params.TestXDPoSMockChainConfigWithV2Engine, 3) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 var extraField utils.ExtraFields_v2 @@ -198,14 +342,14 @@ func TestShouldNotSendVoteMsgIfBlockNotExtendedFromAncestor(t *testing.T) { t.Fatal("Should not trigger vote") case <-time.After(5 * time.Second): // Shoud not trigger setNewRound - round, _, _, _ := engineV2.GetProperties() + 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) + blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 13, params.TestXDPoSMockChainConfigWithV2Engine, 0) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Block 11 is first v2 block @@ -215,7 +359,7 @@ func TestShouldSendVoteMsg(t *testing.T) { if err != nil { t.Fatal(err) } - round, _, _, _ := engineV2.GetProperties() + 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 index 51a125707a..d8796f2aad 100644 --- a/consensus/tests/sync_info_test.go +++ b/consensus/tests/sync_info_test.go @@ -1,6 +1,7 @@ package tests import ( + "math/big" "testing" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" @@ -11,7 +12,7 @@ import ( func TestSyncInfoShouldSuccessfullyUpdateByQC(t *testing.T) { // Block 11 is the first v2 block with starting round of 0 - blockchain, _, currentBlock, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 15, params.TestXDPoSMockChainConfigWithV2Engine, 0) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 15, params.TestXDPoSMockChainConfigWithV2Engine, 0) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 var extraField utils.ExtraFields_v2 @@ -32,15 +33,17 @@ func TestSyncInfoShouldSuccessfullyUpdateByQC(t *testing.T) { if err != nil { t.Fatal(err) } - round, _, highestQuorumCert, _ := engineV2.GetProperties() + round, _, highestQuorumCert, _, highestCommitBlock := engineV2.GetProperties() // QC is parent block's qc, which is pointing at round 4, hence 4 + 1 = 5 assert.Equal(t, utils.Round(5), round) assert.Equal(t, extraField.QuorumCert, highestQuorumCert) + assert.Equal(t, utils.Round(2), highestCommitBlock.Round) + assert.Equal(t, big.NewInt(12), highestCommitBlock.Number) } func TestSyncInfoShouldSuccessfullyUpdateByTC(t *testing.T) { // Block 11 is the first v2 block with starting round of 0 - blockchain, _, currentBlock, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 15, params.TestXDPoSMockChainConfigWithV2Engine, 0) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 15, params.TestXDPoSMockChainConfigWithV2Engine, 0) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 var extraField utils.ExtraFields_v2 @@ -63,7 +66,7 @@ func TestSyncInfoShouldSuccessfullyUpdateByTC(t *testing.T) { if err != nil { t.Fatal(err) } - round, _, highestQuorumCert, _ := engineV2.GetProperties() + round, _, highestQuorumCert, _, _ := engineV2.GetProperties() assert.Equal(t, utils.Round(7), round) assert.Equal(t, extraField.QuorumCert, highestQuorumCert) } diff --git a/consensus/tests/test_helper.go b/consensus/tests/test_helper.go index 688a48011f..c711502131 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, numOfForkedBlocks int) (*BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, *types.Block) { +func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig, numOfForkedBlocks int) (*BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error), *types.Block) { // Preparation var err error backend := getCommonBackend(t, chainConfig) @@ -321,7 +321,7 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon t.Fatal(err) } - return blockchain, backend, currentBlock, signer, currentForkBlock + return blockchain, backend, currentBlock, signer, signFn, currentForkBlock } 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 { diff --git a/consensus/tests/timeout_test.go b/consensus/tests/timeout_test.go index bac2df4d7d..c9b132a7cc 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, 0) + 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, 0) + 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 40c20748ae..8767c6d12f 100644 --- a/consensus/tests/vote_test.go +++ b/consensus/tests/vote_test.go @@ -13,7 +13,7 @@ import ( // VoteHandler func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQCForFistV2Round(t *testing.T) { - blockchain, _, currentBlock, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine, 0) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 11, params.TestXDPoSMockChainConfigWithV2Engine, 0) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 blockInfo := &utils.BlockInfo{ @@ -32,7 +32,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQCForFistV2Round(t *te err := engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) - currentRound, lockQuorumCert, highestQuorumCert, _ := engineV2.GetProperties() + currentRound, lockQuorumCert, highestQuorumCert, _, _ := engineV2.GetProperties() // initialised with nil and 0 round assert.Nil(t, lockQuorumCert) assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round) @@ -43,7 +43,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQCForFistV2Round(t *te } 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.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round) @@ -58,7 +58,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQCForFistV2Round(t *te 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(0), lockQuorumCert.ProposedBlockInfo.Round) // The highestQC proposedBlockInfo shall be the same as the one from its votes @@ -68,7 +68,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQCForFistV2Round(t *te } func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) { - blockchain, _, currentBlock, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 15, params.TestXDPoSMockChainConfigWithV2Engine, 0) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 15, params.TestXDPoSMockChainConfigWithV2Engine, 0) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 blockInfo := &utils.BlockInfo{ @@ -87,7 +87,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) { err := engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) - currentRound, lockQuorumCert, highestQuorumCert, _ := engineV2.GetProperties() + currentRound, lockQuorumCert, highestQuorumCert, _, _ := engineV2.GetProperties() // initialised with nil and 0 round assert.Nil(t, lockQuorumCert) assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round) @@ -98,7 +98,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) { } 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.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round) @@ -113,17 +113,20 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) { err = engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) - currentRound, lockQuorumCert, highestQuorumCert, _ = engineV2.GetProperties() + currentRound, lockQuorumCert, highestQuorumCert, _, highestCommitBlock := 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) + // Should trigger ProcessQC and trying to commit from blockNum of 16's grandgrandparent which is blockNum 13 with round 3 + assert.Equal(t, utils.Round(3), highestCommitBlock.Round) + assert.Equal(t, big.NewInt(13), highestCommitBlock.Number) } func TestThrowErrorIfVoteMsgRoundNotEqualToCurrentRound(t *testing.T) { - blockchain, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 15, params.TestXDPoSMockChainConfigWithV2Engine, 0) + blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 15, params.TestXDPoSMockChainConfigWithV2Engine, 0) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 blockInfo := &utils.BlockInfo{ @@ -153,7 +156,7 @@ func TestThrowErrorIfVoteMsgRoundNotEqualToCurrentRound(t *testing.T) { } func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { - blockchain, _, currentBlock, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 15, params.TestXDPoSMockChainConfigWithV2Engine, 0) + blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 15, params.TestXDPoSMockChainConfigWithV2Engine, 0) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 // Set round to 5 @@ -173,7 +176,7 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { err := engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) - currentRound, lockQuorumCert, highestQuorumCert, _ := engineV2.GetProperties() + currentRound, lockQuorumCert, highestQuorumCert, _, _ := engineV2.GetProperties() // initialised with nil and 0 round assert.Nil(t, lockQuorumCert) assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round) @@ -185,7 +188,7 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { } err = engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) - currentRound, _, _, _ = engineV2.GetProperties() + currentRound, _, _, _, _ = engineV2.GetProperties() assert.Equal(t, utils.Round(5), currentRound) // Create a vote message that should trigger vote pool hook @@ -197,7 +200,7 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { err = engineV2.VoteHandler(blockchain, voteMsg) assert.Nil(t, err) // Check round has now changed from 5 to 6 - 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(4), lockQuorumCert.ProposedBlockInfo.Round) // The highestQC proposedBlockInfo shall be the same as the one from its votes @@ -225,7 +228,7 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { err = engineV2.TimeoutHandler(timeoutMsg) assert.Nil(t, err) - currentRound, _, _, _ = engineV2.GetProperties() + currentRound, _, _, _, _ = engineV2.GetProperties() assert.Equal(t, utils.Round(6), currentRound) timeoutMsg = &utils.Timeout{ Round: utils.Round(6), @@ -233,7 +236,7 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { } err = engineV2.TimeoutHandler(timeoutMsg) assert.Nil(t, err) - currentRound, _, _, _ = engineV2.GetProperties() + currentRound, _, _, _, _ = engineV2.GetProperties() assert.Equal(t, utils.Round(6), currentRound) // Create a timeout message that should trigger timeout pool hook @@ -259,6 +262,6 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { sigatures := []utils.Signature{[]byte{1}, []byte{2}, []byte{3}} assert.ElementsMatch(t, tc.Signatures, sigatures) // Round shall be +1 now - currentRound, _, _, _ = engineV2.GetProperties() + currentRound, _, _, _, _ = engineV2.GetProperties() assert.Equal(t, utils.Round(7), currentRound) } diff --git a/eth/bft/bft_handler.go b/eth/bft/bft_handler.go index 632679037f..4be440d025 100644 --- a/eth/bft/bft_handler.go +++ b/eth/bft/bft_handler.go @@ -19,7 +19,7 @@ type broadcastTimeoutFn func(*utils.Timeout) type broadcastSyncInfoFn func(*utils.SyncInfo) type Bfter struct { - blockCahinReader consensus.ChainReader + blockChainReader consensus.ChainReader broadcastCh chan interface{} quit chan struct{} consensus ConsensusFns @@ -48,7 +48,7 @@ type BroadcastFns struct { SyncInfo broadcastSyncInfoFn } -func New(broadcasts BroadcastFns, blockCahinReader *core.BlockChain) *Bfter { +func New(broadcasts BroadcastFns, blockChainReader *core.BlockChain) *Bfter { knownVotes, _ := lru.New(messageLimit) knownSyncInfos, _ := lru.New(messageLimit) knownTimeouts, _ := lru.New(messageLimit) @@ -59,7 +59,7 @@ func New(broadcasts BroadcastFns, blockCahinReader *core.BlockChain) *Bfter { knownVotes: knownVotes, knownSyncInfos: knownSyncInfos, knownTimeouts: knownTimeouts, - blockCahinReader: blockCahinReader, + blockChainReader: blockChainReader, } } @@ -92,7 +92,7 @@ func (b *Bfter) Vote(vote *utils.Vote) error { } b.broadcastCh <- vote - err = b.consensus.voteHandler(b.blockCahinReader, vote) + err = b.consensus.voteHandler(b.blockChainReader, vote) if err != nil { log.Error("handle BFT Vote", "error", err) return err @@ -137,7 +137,7 @@ func (b *Bfter) SyncInfo(syncInfo *utils.SyncInfo) error { b.broadcastCh <- syncInfo - err = b.consensus.syncInfoHandler(b.blockCahinReader, syncInfo) + err = b.consensus.syncInfoHandler(b.blockChainReader, syncInfo) if err != nil { log.Error("handle BFT SyncInfo", "error", err) return err