mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-21 06:04:33 +00:00
398 lines
16 KiB
Go
398 lines
16 KiB
Go
package engine_v2_tests
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/XinFinOrg/XDPoSChain/accounts/abi/bind/backends"
|
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
|
"github.com/XinFinOrg/XDPoSChain/params"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestShouldSendVoteMsgAndCommitGrandGrandParentBlock(t *testing.T) {
|
|
// Block 901 is the first v2 block with round of 1
|
|
blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 901, params.TestXDPoSMockChainConfig, nil)
|
|
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
|
|
|
var extraField types.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
|
|
poolSize := engineV2.GetVotePoolSizeFaker(voteMsg.(*types.Vote))
|
|
|
|
assert.Equal(t, poolSize, 1)
|
|
assert.NotNil(t, voteMsg)
|
|
assert.Equal(t, currentBlock.Hash(), voteMsg.(*types.Vote).ProposedBlockInfo.Hash)
|
|
|
|
round, _, highestQC, _, _, _ := engineV2.GetPropertiesFaker()
|
|
// Shoud trigger setNewRound
|
|
assert.Equal(t, types.Round(1), round)
|
|
// Should not update the highestQC
|
|
assert.Equal(t, types.Round(0), highestQC.ProposedBlockInfo.Round)
|
|
|
|
// Insert another Block, but it won't trigger commit
|
|
blockNum := 902
|
|
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum)
|
|
block902 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 2, blockCoinBase, signer, signFn, nil, nil, "")
|
|
err = blockchain.InsertBlock(block902)
|
|
assert.Nil(t, err)
|
|
err = engineV2.ProposedBlockHandler(blockchain, block902.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.GetPropertiesFaker()
|
|
// Shoud trigger setNewRound
|
|
assert.Equal(t, types.Round(2), round)
|
|
assert.Equal(t, types.Round(1), highestQC.ProposedBlockInfo.Round)
|
|
|
|
// Insert one more Block, but still won't trigger commit
|
|
blockNum = 903
|
|
blockCoinBase = fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum)
|
|
block903 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block902, blockNum, 3, blockCoinBase, signer, signFn, nil, nil, "")
|
|
err = blockchain.InsertBlock(block903)
|
|
assert.Nil(t, err)
|
|
err = engineV2.ProposedBlockHandler(blockchain, block903.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.GetPropertiesFaker()
|
|
// Shoud NOT trigger setNewRound as the new block parent QC is round 1 but the currentRound is already 2
|
|
assert.Equal(t, types.Round(3), round)
|
|
assert.Equal(t, types.Round(2), highestQC.ProposedBlockInfo.Round)
|
|
assert.Nil(t, highestCommitBlock)
|
|
|
|
// Insert one more Block, this time will trigger commit
|
|
blockNum = 904
|
|
blockCoinBase = fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum)
|
|
block904 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block903, blockNum, 4, blockCoinBase, signer, signFn, nil, nil, "")
|
|
err = blockchain.InsertBlock(block904)
|
|
assert.Nil(t, err)
|
|
err = engineV2.ProposedBlockHandler(blockchain, block904.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.GetPropertiesFaker()
|
|
|
|
assert.Equal(t, types.Round(4), round)
|
|
assert.Equal(t, types.Round(3), highestQC.ProposedBlockInfo.Round)
|
|
assert.Equal(t, currentBlock.Hash(), highestCommitBlock.Hash)
|
|
assert.Equal(t, currentBlock.Number(), highestCommitBlock.Number)
|
|
assert.Equal(t, types.Round(1), highestCommitBlock.Round)
|
|
}
|
|
|
|
func TestShouldNotCommitIfRoundsNotContinousFor3Rounds(t *testing.T) {
|
|
skipLongInShortMode(t)
|
|
// Block 901 is the first v2 block with round of 1
|
|
blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil)
|
|
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
|
|
|
var extraField types.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.(*types.Vote).ProposedBlockInfo.Hash)
|
|
|
|
round, _, highestQC, _, _, highestCommitBlock := engineV2.GetPropertiesFaker()
|
|
|
|
grandGrandParentBlock := blockchain.GetBlockByNumber(902)
|
|
// Shoud trigger setNewRound
|
|
assert.Equal(t, types.Round(5), round)
|
|
assert.Equal(t, types.Round(4), highestQC.ProposedBlockInfo.Round)
|
|
assert.Equal(t, grandGrandParentBlock.Hash(), highestCommitBlock.Hash)
|
|
assert.Equal(t, grandGrandParentBlock.Number(), highestCommitBlock.Number)
|
|
assert.Equal(t, types.Round(2), highestCommitBlock.Round)
|
|
|
|
// Injecting new block which have gaps in the round number (Round 7 instead of 6)
|
|
blockNum := 906
|
|
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum)
|
|
block906 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 7, blockCoinBase, signer, signFn, nil, nil, "")
|
|
err = blockchain.InsertBlock(block906)
|
|
assert.Nil(t, err)
|
|
err = engineV2.ProposedBlockHandler(blockchain, block906.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.GetPropertiesFaker()
|
|
grandGrandParentBlock = blockchain.GetBlockByNumber(903)
|
|
|
|
assert.Equal(t, types.Round(6), round)
|
|
assert.Equal(t, types.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, types.Round(3), highestCommitBlock.Round)
|
|
|
|
blockNum = 907
|
|
blockCoinBase = fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum)
|
|
block907 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block906, blockNum, 8, blockCoinBase, signer, signFn, nil, nil, "")
|
|
err = blockchain.InsertBlock(block907)
|
|
assert.Nil(t, err)
|
|
err = engineV2.ProposedBlockHandler(blockchain, block907.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.GetPropertiesFaker()
|
|
|
|
assert.Equal(t, types.Round(8), round)
|
|
assert.Equal(t, types.Round(7), highestQC.ProposedBlockInfo.Round)
|
|
// Should NOT commit, the `grandGrandParentBlock` is still on blockNum 903
|
|
assert.Equal(t, grandGrandParentBlock.Hash(), highestCommitBlock.Hash)
|
|
assert.Equal(t, grandGrandParentBlock.Number(), highestCommitBlock.Number)
|
|
assert.Equal(t, types.Round(3), highestCommitBlock.Round)
|
|
}
|
|
|
|
func TestProposedBlockMessageHandlerSuccessfullyGenerateVote(t *testing.T) {
|
|
blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 906, params.TestXDPoSMockChainConfig, nil)
|
|
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
|
|
|
// Set current round to 5
|
|
engineV2.SetNewRoundFaker(blockchain, types.Round(5), false)
|
|
|
|
var extraField types.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.(*types.Vote).ProposedBlockInfo.Hash)
|
|
|
|
round, _, highestQC, _, _, _ := engineV2.GetPropertiesFaker()
|
|
// Shoud trigger setNewRound
|
|
assert.Equal(t, types.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, 906, params.TestXDPoSMockChainConfig, nil)
|
|
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
|
|
|
// Set current round to 6
|
|
engineV2.SetNewRoundFaker(blockchain, types.Round(6), false)
|
|
|
|
var extraField types.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.GetPropertiesFaker()
|
|
// Shoud not trigger setNewRound
|
|
assert.Equal(t, types.Round(6), round)
|
|
assert.Equal(t, extraField.QuorumCert.Signatures, highestQC.Signatures)
|
|
}
|
|
|
|
func TestShouldNotSendVoteMessageIfAlreadyVoteForThisRound(t *testing.T) {
|
|
skipLongInShortMode(t)
|
|
blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 906, params.TestXDPoSMockChainConfig, nil)
|
|
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
|
|
|
// Set current round to 5
|
|
engineV2.SetNewRoundFaker(blockchain, types.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.(*types.Vote).ProposedBlockInfo.Hash)
|
|
|
|
round, _, _, _, highestVotedRound, _ := engineV2.GetPropertiesFaker()
|
|
// Shoud trigger setNewRound
|
|
assert.Equal(t, types.Round(6), round)
|
|
assert.Equal(t, types.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(3 * time.Second):
|
|
// Shoud not trigger setNewRound
|
|
round, _, _, _, highestVotedRound, _ = engineV2.GetPropertiesFaker()
|
|
assert.Equal(t, types.Round(6), round)
|
|
assert.Equal(t, types.Round(6), highestVotedRound)
|
|
}
|
|
}
|
|
|
|
func TestShouldNotSendVoteMsgIfBlockInfoRoundNotEqualCurrentRound(t *testing.T) {
|
|
skipLongInShortMode(t)
|
|
blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 906, params.TestXDPoSMockChainConfig, nil)
|
|
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
|
|
|
// Set current round to 8
|
|
engineV2.SetNewRoundFaker(blockchain, types.Round(8), false)
|
|
|
|
var extraField types.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(3 * time.Second):
|
|
// Shoud not trigger setNewRound
|
|
round, _, _, _, _, _ := engineV2.GetPropertiesFaker()
|
|
assert.Equal(t, types.Round(8), round)
|
|
}
|
|
}
|
|
|
|
/*
|
|
Block and round relationship diagram for this test
|
|
... - 13(3) - 14(4) - 15(5) - 16(6)
|
|
\ 14'(7)
|
|
*/
|
|
func TestShouldNotSendVoteMsgIfBlockNotExtendedFromAncestor(t *testing.T) {
|
|
skipLongInShortMode(t)
|
|
// Block number 905, 906 have forks and forkedBlock is the 906th
|
|
var numOfForks = new(int)
|
|
*numOfForks = 3
|
|
blockchain, _, currentBlock, _, _, forkedBlock := PrepareXDCTestBlockChainForV2Engine(t, 906, params.TestXDPoSMockChainConfig, &ForkedBlockOptions{numOfForkedBlocks: numOfForks})
|
|
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
|
|
|
var extraField types.ExtraFields_v2
|
|
err := utils.DecodeBytesExtraFields(forkedBlock.Extra(), &extraField)
|
|
if err != nil {
|
|
t.Fatal("Fail to decode extra data", err)
|
|
}
|
|
assert.Equal(t, types.Round(9), extraField.Round)
|
|
// Set the lockQC and other pre-requist properties by block 906
|
|
err = engineV2.ProposedBlockHandler(blockchain, currentBlock.Header())
|
|
if err != nil {
|
|
t.Fatal("Error while handling block 16", err)
|
|
}
|
|
vote := <-engineV2.BroadcastCh
|
|
assert.Equal(t, types.Round(6), vote.(*types.Vote).ProposedBlockInfo.Round)
|
|
|
|
// Find the first forked block at block 14th
|
|
firstForkedBlock := blockchain.GetBlockByHash(blockchain.GetBlockByHash(forkedBlock.ParentHash()).ParentHash())
|
|
engineV2.SetNewRoundFaker(blockchain, types.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(3 * time.Second):
|
|
// Shoud not trigger setNewRound
|
|
round, _, _, _, _, _ := engineV2.GetPropertiesFaker()
|
|
assert.Equal(t, types.Round(7), round)
|
|
}
|
|
}
|
|
|
|
func TestShouldSendVoteMsg(t *testing.T) {
|
|
// Block number 15, 16 have forks and forkedBlock is the 16th
|
|
blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 903, params.TestXDPoSMockChainConfig, nil)
|
|
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
|
|
|
// Block 901 is first v2 block
|
|
for i := 901; i < 904; i++ {
|
|
blockHeader := blockchain.GetBlockByNumber(uint64(i)).Header()
|
|
err := engineV2.ProposedBlockHandler(blockchain, blockHeader)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
round, _, _, _, _, _ := engineV2.GetPropertiesFaker()
|
|
assert.Equal(t, types.Round(i-900), round)
|
|
vote := <-engineV2.BroadcastCh
|
|
assert.Equal(t, round, vote.(*types.Vote).ProposedBlockInfo.Round)
|
|
}
|
|
}
|
|
|
|
func TestProposedBlockMessageHandlerNotGenerateVoteIfSignerNotInMNlist(t *testing.T) {
|
|
skipLongInShortMode(t)
|
|
blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 906, params.TestXDPoSMockChainConfig, nil)
|
|
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
|
differentSigner, differentSignFn, err := backends.SimulateWalletAddressAndSignFn()
|
|
assert.Nil(t, err)
|
|
// Let's change the address
|
|
engineV2.Authorize(differentSigner, differentSignFn)
|
|
|
|
// Set current round to 5
|
|
engineV2.SetNewRoundFaker(blockchain, types.Round(5), false)
|
|
|
|
var extraField types.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(2 * time.Second):
|
|
// Shoud not trigger setNewRound
|
|
round, _, _, _, _, _ := engineV2.GetPropertiesFaker()
|
|
assert.Equal(t, types.Round(6), round)
|
|
}
|
|
}
|