XIN-159, 160 and 161 (#69)

* XIN-159, 160 and 161

* update the bft handler to make sure we don't process dis-qualified messages

* add verify header missing checks and its tests
This commit is contained in:
Jerome 2022-03-13 22:00:26 +11:00 committed by GitHub
parent a4b362ae9a
commit 9bb1a6e1b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 547 additions and 233 deletions

View file

@ -590,10 +590,13 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade
// Verify this is truely a v2 block first // Verify this is truely a v2 block first
quorumCert, _, _, err := x.getExtraFields(header) quorumCert, round, _, err := x.getExtraFields(header)
if err != nil { if err != nil {
return utils.ErrInvalidV2Extra return utils.ErrInvalidV2Extra
} }
if round <= quorumCert.ProposedBlockInfo.Round {
return utils.ErrRoundInvalid
}
err = x.verifyQC(chain, quorumCert) err = x.verifyQC(chain, quorumCert)
if err != nil { if err != nil {
@ -613,6 +616,10 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade
return utils.ErrInvalidUncleHash return utils.ErrInvalidUncleHash
} }
if header.Difficulty.Cmp(big.NewInt(1)) != 0 {
return utils.ErrInvalidDifficulty
}
isEpochSwitch, _, err := x.IsEpochSwitch(header) // Verify v2 block that is on the epoch switch isEpochSwitch, _, err := x.IsEpochSwitch(header) // Verify v2 block that is on the epoch switch
if err != nil { if err != nil {
log.Error("[verifyHeader] error when checking if header is epoch switch header", "Hash", header.Hash(), "Number", header.Number, "Error", err) log.Error("[verifyHeader] error when checking if header is epoch switch header", "Hash", header.Hash(), "Number", header.Number, "Error", err)
@ -628,6 +635,7 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade
if len(header.Validators)%common.AddressLength != 0 { if len(header.Validators)%common.AddressLength != 0 {
return utils.ErrInvalidCheckpointSigners return utils.ErrInvalidCheckpointSigners
} }
// TODO: Add checkMasternodesOnEpochSwitch
} else { } else {
if len(header.Validators) != 0 { if len(header.Validators) != 0 {
log.Warn("[verifyHeader] Validators shall not have values in non-epochSwitch block", "Hash", header.Hash(), "Number", header.Number, "Validators", header.Validators) log.Warn("[verifyHeader] Validators shall not have values in non-epochSwitch block", "Hash", header.Hash(), "Number", header.Number, "Validators", header.Validators)
@ -652,47 +660,54 @@ func (x *XDPoS_v2) verifyHeader(chain consensus.ChainReader, header *types.Heade
if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash { if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash {
return consensus.ErrUnknownAncestor return consensus.ErrUnknownAncestor
} }
if parent.Time.Uint64()+uint64(x.config.V2.MinePeriod) > header.Time.Uint64() { if parent.Number.Uint64() > x.config.V2.SwitchBlock.Uint64() && parent.Time.Uint64()+uint64(x.config.V2.MinePeriod) > header.Time.Uint64() {
return utils.ErrInvalidTimestamp return utils.ErrInvalidTimestamp
} }
// TODO: verifySeal XIN-135 // TODO: item 9. check validator
_, penalties, err := x.calcMasternodes(chain, header.Number, header.ParentHash)
if err != nil {
log.Error("[verifyHeader] Fail to calculate master nodes list with penalty", "Number", header.Number, "Hash", header.Hash())
return err
}
if !utils.CompareSignersLists(common.ExtractAddressFromBytes(header.Penalties), penalties) {
return utils.ErrPenaltyListDoesNotMatch
}
x.verifiedHeaders.Add(header.Hash(), true) x.verifiedHeaders.Add(header.Hash(), true)
return nil return nil
} }
// Utils for test to get current Pool size
func (x *XDPoS_v2) GetVotePoolSize(vote *utils.Vote) int {
return x.votePool.Size(vote)
}
// Utils for test to get Timeout Pool Size
func (x *XDPoS_v2) GetTimeoutPoolSize(timeout *utils.Timeout) int {
return x.timeoutPool.Size(timeout)
}
/* /*
SyncInfo workflow SyncInfo workflow
*/ */
// Verify syncInfo and trigger process QC or TC if successful // Verify syncInfo and trigger process QC or TC if successful
func (x *XDPoS_v2) VerifySyncInfoMessage(chain consensus.ChainReader, syncInfo *utils.SyncInfo) error { func (x *XDPoS_v2) VerifySyncInfoMessage(chain consensus.ChainReader, syncInfo *utils.SyncInfo) (bool, error) {
/* /*
1. Verify items including: 1. Check QC and TC against highest QC TC. Skip if none of them need to be updated
2. Verify items including:
- verifyQC - verifyQC
- verifyTC - verifyTC
2. Broadcast(Not part of consensus) 3. Broadcast(Not part of consensus)
*/ */
if (x.highestQuorumCert.ProposedBlockInfo.Round >= syncInfo.HighestQuorumCert.ProposedBlockInfo.Round) && (x.highestTimeoutCert.Round >= syncInfo.HighestTimeoutCert.Round) {
log.Warn("[VerifySyncInfoMessage] Round from incoming syncInfo message is no longer qualified", "Highest QC Round", x.highestQuorumCert.ProposedBlockInfo.Round, "Incoming SyncInfo QC Round", syncInfo.HighestQuorumCert.ProposedBlockInfo.Round, "highestTimeoutCert Round", x.highestTimeoutCert.Round, "Incoming syncInfo TC Round", syncInfo.HighestTimeoutCert.Round)
return false, nil
}
err := x.verifyQC(chain, syncInfo.HighestQuorumCert) err := x.verifyQC(chain, syncInfo.HighestQuorumCert)
if err != nil { if err != nil {
log.Warn("SyncInfo message verification failed due to QC", err) log.Warn("SyncInfo message verification failed due to QC", err)
return err return false, err
} }
err = x.verifyTC(chain, syncInfo.HighestTimeoutCert) err = x.verifyTC(chain, syncInfo.HighestTimeoutCert)
if err != nil { if err != nil {
log.Warn("SyncInfo message verification failed due to TC", err) log.Warn("SyncInfo message verification failed due to TC", err)
return err return false, err
} }
return nil return true, nil
} }
func (x *XDPoS_v2) SyncInfoHandler(chain consensus.ChainReader, syncInfo *utils.SyncInfo) error { func (x *XDPoS_v2) SyncInfoHandler(chain consensus.ChainReader, syncInfo *utils.SyncInfo) error {
@ -714,17 +729,19 @@ func (x *XDPoS_v2) SyncInfoHandler(chain consensus.ChainReader, syncInfo *utils.
*/ */
func (x *XDPoS_v2) VerifyVoteMessage(chain consensus.ChainReader, vote *utils.Vote) (bool, error) { func (x *XDPoS_v2) VerifyVoteMessage(chain consensus.ChainReader, vote *utils.Vote) (bool, error) {
/* /*
1. Get masterNode list from snapshot 1. Check vote round with current round for fast fail(disqualifed)
2. Check signature: 2. Get masterNode list from snapshot
3. Check signature:
- Use ecRecover to get the public key - Use ecRecover to get the public key
- Use the above public key to find out the xdc address - Use the above public key to find out the xdc address
- Use the above xdc address to check against the master node list from step 1(For the running epoch) - Use the above xdc address to check against the master node list from step 1(For the running epoch)
4. Broadcast(Not part of consensus) 4. Broadcast(Not part of consensus)
*/ */
err := x.VerifyBlockInfo(chain, vote.ProposedBlockInfo) if vote.ProposedBlockInfo.Round < x.currentRound {
if err != nil { log.Warn("[VerifyVoteMessage] Disqualified vote message as the proposed round does not match currentRound", "vote.ProposedBlockInfo.Round", vote.ProposedBlockInfo.Round, "currentRound", x.currentRound)
return false, err return false, nil
} }
snapshot, err := x.getSnapshot(chain, vote.ProposedBlockInfo.Number.Uint64(), false) snapshot, err := x.getSnapshot(chain, vote.ProposedBlockInfo.Number.Uint64(), false)
if err != nil { if err != nil {
log.Error("[VerifyVoteMessage] fail to get snapshot for a vote message", "BlockNum", vote.ProposedBlockInfo.Number, "Hash", vote.ProposedBlockInfo.Hash, "Error", err.Error()) log.Error("[VerifyVoteMessage] fail to get snapshot for a vote message", "BlockNum", vote.ProposedBlockInfo.Number, "Hash", vote.ProposedBlockInfo.Hash, "Error", err.Error())
@ -770,7 +787,13 @@ func (x *XDPoS_v2) voteHandler(chain consensus.ChainReader, voteMsg *utils.Vote)
return nil return nil
} }
err := x.onVotePoolThresholdReached(chain, pooledVotes, voteMsg, proposedBlockHeader) err := x.VerifyBlockInfo(chain, voteMsg.ProposedBlockInfo)
if err != nil {
x.votePool.ClearPoolKeyByObj(voteMsg)
return err
}
err = x.onVotePoolThresholdReached(chain, pooledVotes, voteMsg, proposedBlockHeader)
if err != nil { if err != nil {
return err return err
} }
@ -1487,41 +1510,6 @@ func (x *XDPoS_v2) isExtendingFromAncestor(blockChainReader consensus.ChainReade
return false, nil return false, nil
} }
/*
Testing tools
*/
func (x *XDPoS_v2) SetNewRoundFaker(blockChainReader consensus.ChainReader, newRound utils.Round, resetTimer bool) {
x.lock.Lock()
defer x.lock.Unlock()
// Reset a bunch of things
if resetTimer {
x.timeoutWorker.Reset(blockChainReader)
}
x.currentRound = newRound
}
// for test only
func (x *XDPoS_v2) ProcessQC(chain consensus.ChainReader, qc *utils.QuorumCert) error {
x.lock.Lock()
defer x.lock.Unlock()
return x.processQC(chain, qc)
}
// Utils for test to check currentRound value
func (x *XDPoS_v2) GetCurrentRound() utils.Round {
x.lock.RLock()
defer x.lock.RUnlock()
return x.currentRound
}
// Utils for test to check currentRound value
func (x *XDPoS_v2) GetProperties() (utils.Round, *utils.QuorumCert, *utils.QuorumCert, utils.Round, *utils.BlockInfo) {
x.lock.RLock()
defer x.lock.RUnlock()
return x.currentRound, x.lockQuorumCert, x.highestQuorumCert, x.highestVotedRound, x.highestCommitBlock
}
// Get master nodes over extra data of epoch switch block. // Get master nodes over extra data of epoch switch block.
func (x *XDPoS_v2) GetMasternodesFromEpochSwitchHeader(epochSwitchHeader *types.Header) []common.Address { func (x *XDPoS_v2) GetMasternodesFromEpochSwitchHeader(epochSwitchHeader *types.Header) []common.Address {
if epochSwitchHeader == nil { if epochSwitchHeader == nil {
@ -1539,7 +1527,7 @@ func (x *XDPoS_v2) GetMasternodesFromEpochSwitchHeader(epochSwitchHeader *types.
func (x *XDPoS_v2) IsEpochSwitch(header *types.Header) (bool, uint64, error) { func (x *XDPoS_v2) IsEpochSwitch(header *types.Header) (bool, uint64, error) {
// Return true directly if we are examing the last v1 block. This could happen if the calling function is examing parent block // Return true directly if we are examing the last v1 block. This could happen if the calling function is examing parent block
if header.Number.Cmp(x.config.V2.SwitchBlock) == 0 { if header.Number.Cmp(x.config.V2.SwitchBlock) == 0 {
log.Info("[IsEpochSwitch] examing last v1 block 👯‍♂️") log.Info("[IsEpochSwitch] examing last v1 block")
return true, header.Number.Uint64() / x.config.Epoch, nil return true, header.Number.Uint64() / x.config.Epoch, nil
} }

View file

@ -0,0 +1,59 @@
package engine_v2
import (
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
)
/*
Testing tools
*/
func (x *XDPoS_v2) SetNewRoundFaker(blockChainReader consensus.ChainReader, newRound utils.Round, resetTimer bool) {
x.lock.Lock()
defer x.lock.Unlock()
// Reset a bunch of things
if resetTimer {
x.timeoutWorker.Reset(blockChainReader)
}
x.currentRound = newRound
}
// for test only
func (x *XDPoS_v2) ProcessQCFaker(chain consensus.ChainReader, qc *utils.QuorumCert) error {
x.lock.Lock()
defer x.lock.Unlock()
return x.processQC(chain, qc)
}
// Utils for test to check currentRound value
func (x *XDPoS_v2) GetCurrentRoundFaker() utils.Round {
x.lock.RLock()
defer x.lock.RUnlock()
return x.currentRound
}
// Utils for test to get current Pool size
func (x *XDPoS_v2) GetVotePoolSizeFaker(vote *utils.Vote) int {
return x.votePool.Size(vote)
}
// Utils for test to get Timeout Pool Size
func (x *XDPoS_v2) GetTimeoutPoolSizeFaker(timeout *utils.Timeout) int {
return x.timeoutPool.Size(timeout)
}
// WARN: This function is designed for testing purpose only!
// Utils for test to check currentRound values
func (x *XDPoS_v2) GetPropertiesFaker() (utils.Round, *utils.QuorumCert, *utils.QuorumCert, *utils.TimeoutCert, utils.Round, *utils.BlockInfo) {
x.lock.RLock()
defer x.lock.RUnlock()
return x.currentRound, x.lockQuorumCert, x.highestQuorumCert, x.highestTimeoutCert, x.highestVotedRound, x.highestCommitBlock
}
// WARN: This function is designed for testing purpose only!
// Utils for tests to set engine specific values
func (x *XDPoS_v2) SetPropertiesFaker(highestQC *utils.QuorumCert, highestTC *utils.TimeoutCert) {
x.highestQuorumCert = highestQC
x.highestTimeoutCert = highestTC
}

View file

@ -85,6 +85,9 @@ var (
ErrInvalidTC = errors.New("Invalid TC content") ErrInvalidTC = errors.New("Invalid TC content")
ErrEmptyBlockInfoHash = errors.New("BlockInfo hash is empty") ErrEmptyBlockInfoHash = errors.New("BlockInfo hash is empty")
ErrInvalidFieldInNonEpochSwitch = errors.New("Invalid field exist in a non-epoch swtich block") ErrInvalidFieldInNonEpochSwitch = errors.New("Invalid field exist in a non-epoch swtich block")
ErrPenaltyListDoesNotMatch = errors.New("Incoming block penalty list does not match")
ErrRoundInvalid = errors.New("Invalid Round, it shall be bigger than QC round")
) )
type ErrIncomingMessageRoundNotEqualCurrentRound struct { type ErrIncomingMessageRoundNotEqualCurrentRound struct {

View file

@ -24,7 +24,7 @@ func TestInitialFirstV2Blcok(t *testing.T) {
err := adaptor.EngineV2.Initial(blockchain, header) err := adaptor.EngineV2.Initial(blockchain, header)
assert.Nil(t, err) assert.Nil(t, err)
round, _, highQC, _, _ := adaptor.EngineV2.GetProperties() round, _, highQC, _, _, _ := adaptor.EngineV2.GetPropertiesFaker()
blockInfo := &utils.BlockInfo{ blockInfo := &utils.BlockInfo{
Hash: header.Hash(), Hash: header.Hash(),
Round: utils.Round(0), Round: utils.Round(0),
@ -98,7 +98,7 @@ func TestInitialOtherV2Block(t *testing.T) {
err = adaptor.EngineV2.Initial(blockchain, block.Header()) err = adaptor.EngineV2.Initial(blockchain, block.Header())
assert.Nil(t, err) assert.Nil(t, err)
round, _, highQC, _, _ := adaptor.EngineV2.GetProperties() round, _, highQC, _, _, _ := adaptor.EngineV2.GetPropertiesFaker()
expectedQuorumCert := &utils.QuorumCert{ expectedQuorumCert := &utils.QuorumCert{
ProposedBlockInfo: blockInfo, ProposedBlockInfo: blockInfo,
Signatures: []utils.Signature{}, Signatures: []utils.Signature{},

View file

@ -47,7 +47,7 @@ func TestYourTurnInitialV2(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
// round=1, so masternode[1] has YourTurn = True // round=1, so masternode[1] has YourTurn = True
assert.True(t, b) assert.True(t, b)
assert.Equal(t, adaptor.EngineV2.GetCurrentRound(), utils.Round(1)) assert.Equal(t, adaptor.EngineV2.GetCurrentRoundFaker(), utils.Round(1))
snap, err := adaptor.EngineV2.GetSnapshot(blockchain, block900.Header()) snap, err := adaptor.EngineV2.GetSnapshot(blockchain, block900.Header())
assert.Nil(t, err) assert.Nil(t, err)

View file

@ -36,7 +36,7 @@ func TestHookPenaltyV2Mining(t *testing.T) {
// set adaptor round/qc to that of 6299 // set adaptor round/qc to that of 6299
err = utils.DecodeBytesExtraFields(header6300.Extra, &extraField) err = utils.DecodeBytesExtraFields(header6300.Extra, &extraField)
assert.Nil(t, err) assert.Nil(t, err)
err = adaptor.EngineV2.ProcessQC(blockchain, extraField.QuorumCert) err = adaptor.EngineV2.ProcessQCFaker(blockchain, extraField.QuorumCert)
assert.Nil(t, err) assert.Nil(t, err)
headerMining := &types.Header{ headerMining := &types.Header{
ParentHash: header6300.ParentHash, ParentHash: header6300.ParentHash,

View file

@ -29,13 +29,13 @@ func TestShouldSendVoteMsgAndCommitGrandGrandParentBlock(t *testing.T) {
} }
voteMsg := <-engineV2.BroadcastCh voteMsg := <-engineV2.BroadcastCh
poolSize := engineV2.GetVotePoolSize(voteMsg.(*utils.Vote)) poolSize := engineV2.GetVotePoolSizeFaker(voteMsg.(*utils.Vote))
assert.Equal(t, poolSize, 1) assert.Equal(t, poolSize, 1)
assert.NotNil(t, voteMsg) assert.NotNil(t, voteMsg)
assert.Equal(t, currentBlock.Hash(), voteMsg.(*utils.Vote).ProposedBlockInfo.Hash) assert.Equal(t, currentBlock.Hash(), voteMsg.(*utils.Vote).ProposedBlockInfo.Hash)
round, _, highestQC, _, _ := engineV2.GetProperties() round, _, highestQC, _, _, _ := engineV2.GetPropertiesFaker()
// Shoud trigger setNewRound // Shoud trigger setNewRound
assert.Equal(t, utils.Round(1), round) assert.Equal(t, utils.Round(1), round)
// Should not update the highestQC // Should not update the highestQC
@ -53,7 +53,7 @@ func TestShouldSendVoteMsgAndCommitGrandGrandParentBlock(t *testing.T) {
// Trigger send vote again but for a new round // Trigger send vote again but for a new round
voteMsg = <-engineV2.BroadcastCh voteMsg = <-engineV2.BroadcastCh
assert.NotNil(t, voteMsg) assert.NotNil(t, voteMsg)
round, _, highestQC, _, _ = engineV2.GetProperties() round, _, highestQC, _, _, _ = engineV2.GetPropertiesFaker()
// Shoud trigger setNewRound // Shoud trigger setNewRound
assert.Equal(t, utils.Round(2), round) assert.Equal(t, utils.Round(2), round)
assert.Equal(t, utils.Round(1), highestQC.ProposedBlockInfo.Round) assert.Equal(t, utils.Round(1), highestQC.ProposedBlockInfo.Round)
@ -70,7 +70,7 @@ func TestShouldSendVoteMsgAndCommitGrandGrandParentBlock(t *testing.T) {
// Trigger send vote again but for a new round // Trigger send vote again but for a new round
voteMsg = <-engineV2.BroadcastCh voteMsg = <-engineV2.BroadcastCh
assert.NotNil(t, voteMsg) assert.NotNil(t, voteMsg)
round, _, highestQC, _, highestCommitBlock := engineV2.GetProperties() round, _, highestQC, _, _, highestCommitBlock := engineV2.GetPropertiesFaker()
// Shoud NOT trigger setNewRound as the new block parent QC is round 1 but the currentRound is already 2 // 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(3), round)
assert.Equal(t, utils.Round(2), highestQC.ProposedBlockInfo.Round) assert.Equal(t, utils.Round(2), highestQC.ProposedBlockInfo.Round)
@ -88,7 +88,7 @@ func TestShouldSendVoteMsgAndCommitGrandGrandParentBlock(t *testing.T) {
// Trigger send vote again but for a new round // Trigger send vote again but for a new round
voteMsg = <-engineV2.BroadcastCh voteMsg = <-engineV2.BroadcastCh
assert.NotNil(t, voteMsg) assert.NotNil(t, voteMsg)
round, _, highestQC, _, highestCommitBlock = engineV2.GetProperties() round, _, highestQC, _, _, highestCommitBlock = engineV2.GetPropertiesFaker()
assert.Equal(t, utils.Round(4), round) assert.Equal(t, utils.Round(4), round)
assert.Equal(t, utils.Round(3), highestQC.ProposedBlockInfo.Round) assert.Equal(t, utils.Round(3), highestQC.ProposedBlockInfo.Round)
@ -117,7 +117,7 @@ func TestShouldNotCommitIfRoundsNotContinousFor3Rounds(t *testing.T) {
assert.NotNil(t, voteMsg) assert.NotNil(t, voteMsg)
assert.Equal(t, currentBlock.Hash(), voteMsg.(*utils.Vote).ProposedBlockInfo.Hash) assert.Equal(t, currentBlock.Hash(), voteMsg.(*utils.Vote).ProposedBlockInfo.Hash)
round, _, highestQC, _, highestCommitBlock := engineV2.GetProperties() round, _, highestQC, _, _, highestCommitBlock := engineV2.GetPropertiesFaker()
grandGrandParentBlock := blockchain.GetBlockByNumber(902) grandGrandParentBlock := blockchain.GetBlockByNumber(902)
// Shoud trigger setNewRound // Shoud trigger setNewRound
@ -139,7 +139,7 @@ func TestShouldNotCommitIfRoundsNotContinousFor3Rounds(t *testing.T) {
// Trigger send vote again but for a new round // Trigger send vote again but for a new round
voteMsg = <-engineV2.BroadcastCh voteMsg = <-engineV2.BroadcastCh
assert.NotNil(t, voteMsg) assert.NotNil(t, voteMsg)
round, _, highestQC, _, highestCommitBlock = engineV2.GetProperties() round, _, highestQC, _, _, highestCommitBlock = engineV2.GetPropertiesFaker()
grandGrandParentBlock = blockchain.GetBlockByNumber(903) grandGrandParentBlock = blockchain.GetBlockByNumber(903)
assert.Equal(t, utils.Round(6), round) assert.Equal(t, utils.Round(6), round)
@ -160,7 +160,7 @@ func TestShouldNotCommitIfRoundsNotContinousFor3Rounds(t *testing.T) {
// Trigger send vote again but for a new round // Trigger send vote again but for a new round
voteMsg = <-engineV2.BroadcastCh voteMsg = <-engineV2.BroadcastCh
assert.NotNil(t, voteMsg) assert.NotNil(t, voteMsg)
round, _, highestQC, _, highestCommitBlock = engineV2.GetProperties() round, _, highestQC, _, _, highestCommitBlock = engineV2.GetPropertiesFaker()
assert.Equal(t, utils.Round(8), round) assert.Equal(t, utils.Round(8), round)
assert.Equal(t, utils.Round(7), highestQC.ProposedBlockInfo.Round) assert.Equal(t, utils.Round(7), highestQC.ProposedBlockInfo.Round)
@ -193,7 +193,7 @@ func TestProposedBlockMessageHandlerSuccessfullyGenerateVote(t *testing.T) {
assert.NotNil(t, voteMsg) assert.NotNil(t, voteMsg)
assert.Equal(t, currentBlock.Hash(), voteMsg.(*utils.Vote).ProposedBlockInfo.Hash) assert.Equal(t, currentBlock.Hash(), voteMsg.(*utils.Vote).ProposedBlockInfo.Hash)
round, _, highestQC, _, _ := engineV2.GetProperties() round, _, highestQC, _, _, _ := engineV2.GetPropertiesFaker()
// Shoud trigger setNewRound // Shoud trigger setNewRound
assert.Equal(t, utils.Round(6), round) assert.Equal(t, utils.Round(6), round)
assert.Equal(t, extraField.QuorumCert.Signatures, highestQC.Signatures) assert.Equal(t, extraField.QuorumCert.Signatures, highestQC.Signatures)
@ -219,7 +219,7 @@ func TestShouldNotSetNewRound(t *testing.T) {
t.Fatal("Fail propose proposedBlock handler", err) t.Fatal("Fail propose proposedBlock handler", err)
} }
round, _, highestQC, _, _ := engineV2.GetProperties() round, _, highestQC, _, _, _ := engineV2.GetPropertiesFaker()
// Shoud not trigger setNewRound // Shoud not trigger setNewRound
assert.Equal(t, utils.Round(6), round) assert.Equal(t, utils.Round(6), round)
assert.Equal(t, extraField.QuorumCert.Signatures, highestQC.Signatures) assert.Equal(t, extraField.QuorumCert.Signatures, highestQC.Signatures)
@ -241,7 +241,7 @@ func TestShouldNotSendVoteMessageIfAlreadyVoteForThisRound(t *testing.T) {
assert.NotNil(t, voteMsg) assert.NotNil(t, voteMsg)
assert.Equal(t, currentBlock.Hash(), voteMsg.(*utils.Vote).ProposedBlockInfo.Hash) assert.Equal(t, currentBlock.Hash(), voteMsg.(*utils.Vote).ProposedBlockInfo.Hash)
round, _, _, highestVotedRound, _ := engineV2.GetProperties() round, _, _, _, highestVotedRound, _ := engineV2.GetPropertiesFaker()
// Shoud trigger setNewRound // Shoud trigger setNewRound
assert.Equal(t, utils.Round(6), round) assert.Equal(t, utils.Round(6), round)
assert.Equal(t, utils.Round(6), highestVotedRound) assert.Equal(t, utils.Round(6), highestVotedRound)
@ -257,7 +257,7 @@ func TestShouldNotSendVoteMessageIfAlreadyVoteForThisRound(t *testing.T) {
t.Fatal("Should not trigger vote") t.Fatal("Should not trigger vote")
case <-time.After(5 * time.Second): case <-time.After(5 * time.Second):
// Shoud not trigger setNewRound // Shoud not trigger setNewRound
round, _, _, highestVotedRound, _ = engineV2.GetProperties() round, _, _, _, highestVotedRound, _ = engineV2.GetPropertiesFaker()
assert.Equal(t, utils.Round(6), round) assert.Equal(t, utils.Round(6), round)
assert.Equal(t, utils.Round(6), highestVotedRound) assert.Equal(t, utils.Round(6), highestVotedRound)
} }
@ -286,7 +286,7 @@ func TestShouldNotSendVoteMsgIfBlockInfoRoundNotEqualCurrentRound(t *testing.T)
t.Fatal("Should not trigger vote") t.Fatal("Should not trigger vote")
case <-time.After(5 * time.Second): case <-time.After(5 * time.Second):
// Shoud not trigger setNewRound // Shoud not trigger setNewRound
round, _, _, _, _ := engineV2.GetProperties() round, _, _, _, _, _ := engineV2.GetPropertiesFaker()
assert.Equal(t, utils.Round(8), round) assert.Equal(t, utils.Round(8), round)
} }
} }
@ -328,7 +328,7 @@ func TestShouldNotSendVoteMsgIfBlockNotExtendedFromAncestor(t *testing.T) {
t.Fatal("Should not trigger vote") t.Fatal("Should not trigger vote")
case <-time.After(5 * time.Second): case <-time.After(5 * time.Second):
// Shoud not trigger setNewRound // Shoud not trigger setNewRound
round, _, _, _, _ := engineV2.GetProperties() round, _, _, _, _, _ := engineV2.GetPropertiesFaker()
assert.Equal(t, utils.Round(7), round) assert.Equal(t, utils.Round(7), round)
} }
} }
@ -345,7 +345,7 @@ func TestShouldSendVoteMsg(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
round, _, _, _, _ := engineV2.GetProperties() round, _, _, _, _, _ := engineV2.GetPropertiesFaker()
assert.Equal(t, utils.Round(i-900), round) assert.Equal(t, utils.Round(i-900), round)
vote := <-engineV2.BroadcastCh vote := <-engineV2.BroadcastCh
assert.Equal(t, round, vote.(*utils.Vote).ProposedBlockInfo.Round) assert.Equal(t, round, vote.(*utils.Vote).ProposedBlockInfo.Round)

View file

@ -33,7 +33,7 @@ func TestSyncInfoShouldSuccessfullyUpdateByQC(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
round, _, highestQuorumCert, _, highestCommitBlock := engineV2.GetProperties() round, _, highestQuorumCert, _, _, highestCommitBlock := engineV2.GetPropertiesFaker()
// QC is parent block's qc, which is pointing at round 4, hence 4 + 1 = 5 // 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, utils.Round(5), round)
assert.Equal(t, extraField.QuorumCert, highestQuorumCert) assert.Equal(t, extraField.QuorumCert, highestQuorumCert)
@ -66,7 +66,36 @@ func TestSyncInfoShouldSuccessfullyUpdateByTC(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
round, _, highestQuorumCert, _, _ := engineV2.GetProperties() round, _, highestQuorumCert, _, _, _ := engineV2.GetPropertiesFaker()
assert.Equal(t, utils.Round(7), round) assert.Equal(t, utils.Round(7), round)
assert.Equal(t, extraField.QuorumCert, highestQuorumCert) assert.Equal(t, extraField.QuorumCert, highestQuorumCert)
} }
func TestSkipVerifySyncInfoIfBothQcTcNotQualified(t *testing.T) {
blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, 0)
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
// Make the Highest QC in syncInfo point to an old block to simulate it's no longer qualified
parentBlock := blockchain.GetBlockByNumber(903)
var extraField utils.ExtraFields_v2
err := utils.DecodeBytesExtraFields(parentBlock.Extra(), &extraField)
if err != nil {
t.Fatal("Fail to decode extra data", err)
}
highestTC := &utils.TimeoutCert{
Round: utils.Round(5),
Signatures: []utils.Signature{},
}
syncInfoMsg := &utils.SyncInfo{
HighestQuorumCert: extraField.QuorumCert,
HighestTimeoutCert: highestTC,
}
engineV2.SetPropertiesFaker(syncInfoMsg.HighestQuorumCert, syncInfoMsg.HighestTimeoutCert)
verified, err := engineV2.VerifySyncInfoMessage(blockchain, syncInfoMsg)
assert.False(t, verified)
assert.Nil(t, err)
}

View file

@ -19,7 +19,7 @@ func TestCountdownTimeoutToSendTimeoutMessage(t *testing.T) {
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
timeoutMsg := <-engineV2.BroadcastCh timeoutMsg := <-engineV2.BroadcastCh
poolSize := engineV2.GetTimeoutPoolSize(timeoutMsg.(*utils.Timeout)) poolSize := engineV2.GetTimeoutPoolSizeFaker(timeoutMsg.(*utils.Timeout))
assert.Equal(t, poolSize, 1) assert.Equal(t, poolSize, 1)
assert.NotNil(t, timeoutMsg) assert.NotNil(t, timeoutMsg)
assert.Equal(t, uint64(1350), timeoutMsg.(*utils.Timeout).GapNumber) assert.Equal(t, uint64(1350), timeoutMsg.(*utils.Timeout).GapNumber)
@ -99,7 +99,7 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) {
err := engineV2.TimeoutHandler(blockchain, timeoutMsg) err := engineV2.TimeoutHandler(blockchain, timeoutMsg)
assert.Nil(t, err) assert.Nil(t, err)
currentRound, _, _, _, _ := engineV2.GetProperties() currentRound, _, _, _, _, _ := engineV2.GetPropertiesFaker()
assert.Equal(t, utils.Round(1), currentRound) assert.Equal(t, utils.Round(1), currentRound)
timeoutMsg = &utils.Timeout{ timeoutMsg = &utils.Timeout{
Round: utils.Round(1), Round: utils.Round(1),
@ -108,7 +108,7 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) {
} }
err = engineV2.TimeoutHandler(blockchain, timeoutMsg) err = engineV2.TimeoutHandler(blockchain, timeoutMsg)
assert.Nil(t, err) assert.Nil(t, err)
currentRound, _, _, _, _ = engineV2.GetProperties() currentRound, _, _, _, _, _ = engineV2.GetPropertiesFaker()
assert.Equal(t, utils.Round(1), currentRound) assert.Equal(t, utils.Round(1), currentRound)
// Send a timeout with different gap number, it shall not trigger timeout pool hook // Send a timeout with different gap number, it shall not trigger timeout pool hook
@ -119,7 +119,7 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) {
} }
err = engineV2.TimeoutHandler(blockchain, timeoutMsg) err = engineV2.TimeoutHandler(blockchain, timeoutMsg)
assert.Nil(t, err) assert.Nil(t, err)
currentRound, _, _, _, _ = engineV2.GetProperties() currentRound, _, _, _, _, _ = engineV2.GetPropertiesFaker()
assert.Equal(t, utils.Round(1), currentRound) assert.Equal(t, utils.Round(1), currentRound)
// Create a timeout message that should trigger timeout pool hook // Create a timeout message that should trigger timeout pool hook
@ -134,7 +134,7 @@ func TestTimeoutMessageHandlerSuccessfullyGenerateTCandSyncInfo(t *testing.T) {
syncInfoMsg := <-engineV2.BroadcastCh syncInfoMsg := <-engineV2.BroadcastCh
currentRound, _, _, _, _ = engineV2.GetProperties() currentRound, _, _, _, _, _ = engineV2.GetPropertiesFaker()
assert.NotNil(t, syncInfoMsg) assert.NotNil(t, syncInfoMsg)

View file

@ -1,91 +0,0 @@
package tests
import (
"encoding/json"
"fmt"
"testing"
"github.com/XinFinOrg/XDPoSChain/accounts"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/stretchr/testify/assert"
)
func TestShouldVerifyBlock(t *testing.T) {
b, err := json.Marshal(params.TestXDPoSMockChainConfig)
assert.Nil(t, err)
configString := string(b)
var config params.ChainConfig
err = json.Unmarshal([]byte(configString), &config)
assert.Nil(t, err)
// Enable verify
config.XDPoS.V2.SkipV2Validation = false
// Skip the mining time validation by set mine time to 0
config.XDPoS.V2.MinePeriod = 0
// Block 901 is the first v2 block with round of 1
blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, &config, 0)
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
// Happy path
err = adaptor.VerifyHeader(blockchain, blockchain.GetBlockByNumber(901).Header(), true)
assert.Nil(t, err)
// Verify non-epoch switch block
err = adaptor.VerifyHeader(blockchain, blockchain.GetBlockByNumber(902).Header(), true)
assert.Nil(t, err)
nonEpochSwitchWithValidators := blockchain.GetBlockByNumber(902).Header()
nonEpochSwitchWithValidators.Validators = acc1Addr.Bytes()
err = adaptor.VerifyHeader(blockchain, nonEpochSwitchWithValidators, true)
assert.Equal(t, utils.ErrInvalidFieldInNonEpochSwitch, err)
}
func TestShouldFailIfNotEnoughQCSignatures(t *testing.T) {
b, err := json.Marshal(params.TestXDPoSMockChainConfig)
assert.Nil(t, err)
configString := string(b)
var config params.ChainConfig
err = json.Unmarshal([]byte(configString), &config)
assert.Nil(t, err)
// Enable verify
config.XDPoS.V2.SkipV2Validation = false
// Skip the mining time validation by set mine time to 0
config.XDPoS.V2.MinePeriod = 0
// Block 901 is the first v2 block with round of 1
blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 902, &config, 0)
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
parentBlock := blockchain.GetBlockByNumber(901)
proposedBlockInfo := &utils.BlockInfo{
Hash: parentBlock.Hash(),
Round: utils.Round(1),
Number: parentBlock.Number(),
}
signedHash, err := signFn(accounts.Account{Address: signer}, utils.VoteSigHash(proposedBlockInfo).Bytes())
assert.Nil(t, err)
var signatures []utils.Signature
// Duplicate the signatures
signatures = append(signatures, signedHash, signedHash, signedHash, signedHash, signedHash, signedHash)
quorumCert := &utils.QuorumCert{
ProposedBlockInfo: proposedBlockInfo,
Signatures: signatures,
}
extra := utils.ExtraFields_v2{
Round: utils.Round(2),
QuorumCert: quorumCert,
}
extraInBytes, err := extra.EncodeToBytes()
if err != nil {
panic(fmt.Errorf("Error encode extra into bytes: %v", err))
}
headerWithDuplicatedSignatures := currentBlock.Header()
headerWithDuplicatedSignatures.Extra = extraInBytes
// Happy path
err = adaptor.VerifyHeader(blockchain, headerWithDuplicatedSignatures, true)
assert.Equal(t, utils.ErrInvalidQC, err)
}

View file

@ -0,0 +1,193 @@
package tests
import (
"encoding/json"
"fmt"
"math/big"
"testing"
"time"
"github.com/XinFinOrg/XDPoSChain/accounts"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus"
"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 TestShouldVerifyBlock(t *testing.T) {
b, err := json.Marshal(params.TestXDPoSMockChainConfig)
assert.Nil(t, err)
configString := string(b)
var config params.ChainConfig
err = json.Unmarshal([]byte(configString), &config)
assert.Nil(t, err)
// Enable verify
config.XDPoS.V2.SkipV2Validation = false
// Skip the mining time validation by set mine time to 0
config.XDPoS.V2.MinePeriod = 0
// Block 901 is the first v2 block with round of 1
blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 910, &config, 0)
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
// Happy path
err = adaptor.VerifyHeader(blockchain, blockchain.GetBlockByNumber(901).Header(), true)
assert.Nil(t, err)
// Unhappy path
// Verify non-epoch switch block
err = adaptor.VerifyHeader(blockchain, blockchain.GetBlockByNumber(902).Header(), true)
assert.Nil(t, err)
nonEpochSwitchWithValidators := blockchain.GetBlockByNumber(902).Header()
nonEpochSwitchWithValidators.Validators = acc1Addr.Bytes()
err = adaptor.VerifyHeader(blockchain, nonEpochSwitchWithValidators, true)
assert.Equal(t, utils.ErrInvalidFieldInNonEpochSwitch, err)
noValidatorBlock := blockchain.GetBlockByNumber(902).Header()
noValidatorBlock.Validator = []byte{}
err = adaptor.VerifyHeader(blockchain, noValidatorBlock, true)
assert.Equal(t, consensus.ErrNoValidatorSignature, err)
blockFromFuture := blockchain.GetBlockByNumber(902).Header()
blockFromFuture.Time = big.NewInt(time.Now().Unix() + 10000)
err = adaptor.VerifyHeader(blockchain, blockFromFuture, true)
assert.Equal(t, consensus.ErrFutureBlock, err)
invalidQcBlock := blockchain.GetBlockByNumber(902).Header()
invalidQcBlock.Extra = []byte{}
err = adaptor.VerifyHeader(blockchain, invalidQcBlock, true)
assert.Equal(t, utils.ErrInvalidV2Extra, err)
// Epoch switch
invalidAuthNonceBlock := blockchain.GetBlockByNumber(901).Header()
invalidAuthNonceBlock.Nonce = types.BlockNonce{123}
err = adaptor.VerifyHeader(blockchain, invalidAuthNonceBlock, true)
assert.Equal(t, utils.ErrInvalidVote, err)
emptyValidatorsBlock := blockchain.GetBlockByNumber(901).Header()
emptyValidatorsBlock.Validators = []byte{}
err = adaptor.VerifyHeader(blockchain, emptyValidatorsBlock, true)
assert.Equal(t, utils.ErrEmptyEpochSwitchValidators, err)
invalidValidatorsSignerBlock := blockchain.GetBlockByNumber(901).Header()
invalidValidatorsSignerBlock.Validators = []byte{123}
err = adaptor.VerifyHeader(blockchain, invalidValidatorsSignerBlock, true)
assert.Equal(t, utils.ErrInvalidCheckpointSigners, err)
// non-epoch switch
invalidValidatorsExistBlock := blockchain.GetBlockByNumber(902).Header()
invalidValidatorsExistBlock.Validators = []byte{123}
err = adaptor.VerifyHeader(blockchain, invalidValidatorsExistBlock, true)
assert.Equal(t, utils.ErrInvalidFieldInNonEpochSwitch, err)
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb123"
parentNotExistBlock := blockchain.GetBlockByNumber(901).Header()
parentNotExistBlock.ParentHash = common.HexToHash(merkleRoot)
err = adaptor.VerifyHeader(blockchain, parentNotExistBlock, true)
assert.Equal(t, consensus.ErrUnknownAncestor, err)
tooFastMinedBlock := blockchain.GetBlockByNumber(902).Header()
tooFastMinedBlock.Time = big.NewInt(time.Now().Unix() - 2)
err = adaptor.VerifyHeader(blockchain, tooFastMinedBlock, true)
assert.Equal(t, utils.ErrInvalidTimestamp, err)
invalidDifficultyBlock := blockchain.GetBlockByNumber(902).Header()
invalidDifficultyBlock.Difficulty = big.NewInt(2)
err = adaptor.VerifyHeader(blockchain, invalidDifficultyBlock, true)
assert.Equal(t, utils.ErrInvalidDifficulty, err)
// Creat an invalid QC round
proposedBlockInfo := &utils.BlockInfo{
Hash: blockchain.GetBlockByNumber(902).Hash(),
Round: utils.Round(2),
Number: blockchain.GetBlockByNumber(902).Number(),
}
// Genrate QC
signedHash, err := signFn(accounts.Account{Address: signer}, utils.VoteSigHash(proposedBlockInfo).Bytes())
if err != nil {
panic(fmt.Errorf("Error generate QC by creating signedHash: %v", err))
}
// Sign from acc 1, 2, 3
acc1SignedHash := SignHashByPK(acc1Key, utils.VoteSigHash(proposedBlockInfo).Bytes())
acc2SignedHash := SignHashByPK(acc2Key, utils.VoteSigHash(proposedBlockInfo).Bytes())
acc3SignedHash := SignHashByPK(acc3Key, utils.VoteSigHash(proposedBlockInfo).Bytes())
var signatures []utils.Signature
signatures = append(signatures, signedHash, acc1SignedHash, acc2SignedHash, acc3SignedHash)
quorumCert := &utils.QuorumCert{
ProposedBlockInfo: proposedBlockInfo,
Signatures: signatures,
}
extra := utils.ExtraFields_v2{
Round: utils.Round(2),
QuorumCert: quorumCert,
}
extraInBytes, err := extra.EncodeToBytes()
if err != nil {
panic(fmt.Errorf("Error encode extra into bytes: %v", err))
}
invalidRoundBlock := blockchain.GetBlockByNumber(902).Header()
invalidRoundBlock.Extra = extraInBytes
err = adaptor.VerifyHeader(blockchain, invalidRoundBlock, true)
assert.Equal(t, utils.ErrRoundInvalid, err)
invalidPenaltiesExistBlock := blockchain.GetBlockByNumber(902).Header()
invalidPenaltiesExistBlock.Penalties = common.Hex2BytesFixed("123131231", 20)
err = adaptor.VerifyHeader(blockchain, invalidPenaltiesExistBlock, true)
assert.Equal(t, utils.ErrPenaltyListDoesNotMatch, err)
}
func TestShouldFailIfNotEnoughQCSignatures(t *testing.T) {
b, err := json.Marshal(params.TestXDPoSMockChainConfig)
assert.Nil(t, err)
configString := string(b)
var config params.ChainConfig
err = json.Unmarshal([]byte(configString), &config)
assert.Nil(t, err)
// Enable verify
config.XDPoS.V2.SkipV2Validation = false
// Skip the mining time validation by set mine time to 0
config.XDPoS.V2.MinePeriod = 0
// Block 901 is the first v2 block with round of 1
blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 902, &config, 0)
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
parentBlock := blockchain.GetBlockByNumber(901)
proposedBlockInfo := &utils.BlockInfo{
Hash: parentBlock.Hash(),
Round: utils.Round(1),
Number: parentBlock.Number(),
}
signedHash, err := signFn(accounts.Account{Address: signer}, utils.VoteSigHash(proposedBlockInfo).Bytes())
assert.Nil(t, err)
var signatures []utils.Signature
// Duplicate the signatures
signatures = append(signatures, signedHash, signedHash, signedHash, signedHash, signedHash, signedHash)
quorumCert := &utils.QuorumCert{
ProposedBlockInfo: proposedBlockInfo,
Signatures: signatures,
}
extra := utils.ExtraFields_v2{
Round: utils.Round(2),
QuorumCert: quorumCert,
}
extraInBytes, err := extra.EncodeToBytes()
if err != nil {
panic(fmt.Errorf("Error encode extra into bytes: %v", err))
}
headerWithDuplicatedSignatures := currentBlock.Header()
headerWithDuplicatedSignatures.Extra = extraInBytes
// Happy path
err = adaptor.VerifyHeader(blockchain, headerWithDuplicatedSignatures, true)
assert.Equal(t, utils.ErrInvalidQC, err)
}

View file

@ -38,7 +38,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQCForFistV2Round(t *te
err = engineV2.VoteHandler(blockchain, voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) assert.Nil(t, err)
currentRound, lockQuorumCert, highestQuorumCert, _, _ := engineV2.GetProperties() currentRound, lockQuorumCert, highestQuorumCert, _, _, _ := engineV2.GetPropertiesFaker()
// initialised with nil and 0 round // initialised with nil and 0 round
assert.Nil(t, lockQuorumCert) assert.Nil(t, lockQuorumCert)
assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round) assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round)
@ -52,7 +52,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQCForFistV2Round(t *te
} }
err = engineV2.VoteHandler(blockchain, voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) assert.Nil(t, err)
currentRound, lockQuorumCert, highestQuorumCert, _, _ = engineV2.GetProperties() currentRound, lockQuorumCert, highestQuorumCert, _, _, _ = engineV2.GetPropertiesFaker()
// Still using the initlised value because we did not yet go to the next round // Still using the initlised value because we did not yet go to the next round
assert.Nil(t, lockQuorumCert) assert.Nil(t, lockQuorumCert)
assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round) assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round)
@ -68,7 +68,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQCForFistV2Round(t *te
err = engineV2.VoteHandler(blockchain, voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) assert.Nil(t, err)
currentRound, lockQuorumCert, highestQuorumCert, _, _ = engineV2.GetProperties() currentRound, lockQuorumCert, highestQuorumCert, _, _, _ = engineV2.GetPropertiesFaker()
// The lockQC shall be the parent's QC round number // The lockQC shall be the parent's QC round number
assert.Equal(t, utils.Round(0), 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 // The highestQC proposedBlockInfo shall be the same as the one from its votes
@ -100,7 +100,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) {
err = engineV2.VoteHandler(blockchain, voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) assert.Nil(t, err)
currentRound, lockQuorumCert, highestQuorumCert, _, _ := engineV2.GetProperties() currentRound, lockQuorumCert, highestQuorumCert, _, _, _ := engineV2.GetPropertiesFaker()
// initialised with nil and 0 round // initialised with nil and 0 round
assert.Nil(t, lockQuorumCert) assert.Nil(t, lockQuorumCert)
assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round) assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round)
@ -112,7 +112,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) {
} }
err = engineV2.VoteHandler(blockchain, voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) assert.Nil(t, err)
currentRound, lockQuorumCert, highestQuorumCert, _, _ = engineV2.GetProperties() currentRound, lockQuorumCert, highestQuorumCert, _, _, _ = engineV2.GetPropertiesFaker()
// Still using the initlised value because we did not yet go to the next round // Still using the initlised value because we did not yet go to the next round
assert.Nil(t, lockQuorumCert) assert.Nil(t, lockQuorumCert)
assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round) assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round)
@ -130,7 +130,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) {
} }
err = engineV2.VoteHandler(blockchain, voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) assert.Nil(t, err)
currentRound, lockQuorumCert, highestQuorumCert, _, _ = engineV2.GetProperties() currentRound, lockQuorumCert, highestQuorumCert, _, _, _ = engineV2.GetPropertiesFaker()
// Still using the initlised value because we did not yet go to the next round // Still using the initlised value because we did not yet go to the next round
assert.Nil(t, lockQuorumCert) assert.Nil(t, lockQuorumCert)
assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round) assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round)
@ -145,7 +145,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) {
err = engineV2.VoteHandler(blockchain, voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) assert.Nil(t, err)
currentRound, lockQuorumCert, highestQuorumCert, _, highestCommitBlock := engineV2.GetProperties() currentRound, lockQuorumCert, highestQuorumCert, _, _, highestCommitBlock := engineV2.GetPropertiesFaker()
// The lockQC shall be the parent's QC round number // The lockQC shall be the parent's QC round number
assert.Equal(t, utils.Round(4), 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 // The highestQC proposedBlockInfo shall be the same as the one from its votes
@ -202,7 +202,7 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
blockInfo := &utils.BlockInfo{ blockInfo := &utils.BlockInfo{
Hash: currentBlock.Hash(), Hash: currentBlock.Hash(),
Round: utils.Round(5), Round: utils.Round(5),
Number: big.NewInt(901), Number: big.NewInt(905),
} }
voteSigningHash := utils.VoteSigHash(blockInfo) voteSigningHash := utils.VoteSigHash(blockInfo)
// Create two vote message which will not reach vote pool threshold // Create two vote message which will not reach vote pool threshold
@ -214,7 +214,7 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
err := engineV2.VoteHandler(blockchain, voteMsg) err := engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) assert.Nil(t, err)
currentRound, lockQuorumCert, highestQuorumCert, _, _ := engineV2.GetProperties() currentRound, lockQuorumCert, highestQuorumCert, _, _, _ := engineV2.GetPropertiesFaker()
// initialised with nil and 0 round // initialised with nil and 0 round
assert.Nil(t, lockQuorumCert) assert.Nil(t, lockQuorumCert)
assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round) assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round)
@ -226,7 +226,7 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
} }
err = engineV2.VoteHandler(blockchain, voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) assert.Nil(t, err)
currentRound, _, _, _, _ = engineV2.GetProperties() currentRound, _, _, _, _, _ = engineV2.GetPropertiesFaker()
assert.Equal(t, utils.Round(5), currentRound) assert.Equal(t, utils.Round(5), currentRound)
// Create a vote message that should trigger vote pool hook // Create a vote message that should trigger vote pool hook
@ -238,7 +238,7 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
err = engineV2.VoteHandler(blockchain, voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) assert.Nil(t, err)
// Check round has now changed from 5 to 6 // Check round has now changed from 5 to 6
currentRound, lockQuorumCert, highestQuorumCert, _, _ = engineV2.GetProperties() currentRound, lockQuorumCert, highestQuorumCert, _, _, _ = engineV2.GetPropertiesFaker()
// The lockQC shall be the parent's QC round number // The lockQC shall be the parent's QC round number
assert.Equal(t, utils.Round(4), 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 // The highestQC proposedBlockInfo shall be the same as the one from its votes
@ -266,7 +266,7 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
err = engineV2.TimeoutHandler(blockchain, timeoutMsg) err = engineV2.TimeoutHandler(blockchain, timeoutMsg)
assert.Nil(t, err) assert.Nil(t, err)
currentRound, _, _, _, _ = engineV2.GetProperties() currentRound, _, _, _, _, _ = engineV2.GetPropertiesFaker()
assert.Equal(t, utils.Round(6), currentRound) assert.Equal(t, utils.Round(6), currentRound)
timeoutMsg = &utils.Timeout{ timeoutMsg = &utils.Timeout{
Round: utils.Round(6), Round: utils.Round(6),
@ -274,7 +274,7 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
} }
err = engineV2.TimeoutHandler(blockchain, timeoutMsg) err = engineV2.TimeoutHandler(blockchain, timeoutMsg)
assert.Nil(t, err) assert.Nil(t, err)
currentRound, _, _, _, _ = engineV2.GetProperties() currentRound, _, _, _, _, _ = engineV2.GetPropertiesFaker()
assert.Equal(t, utils.Round(6), currentRound) assert.Equal(t, utils.Round(6), currentRound)
// Create a timeout message that should trigger timeout pool hook // Create a timeout message that should trigger timeout pool hook
@ -300,7 +300,7 @@ func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) {
sigatures := []utils.Signature{[]byte{1}, []byte{2}, []byte{3}} sigatures := []utils.Signature{[]byte{1}, []byte{2}, []byte{3}}
assert.ElementsMatch(t, tc.Signatures, sigatures) assert.ElementsMatch(t, tc.Signatures, sigatures)
// Round shall be +1 now // Round shall be +1 now
currentRound, _, _, _, _ = engineV2.GetProperties() currentRound, _, _, _, _, _ = engineV2.GetPropertiesFaker()
assert.Equal(t, utils.Round(7), currentRound) assert.Equal(t, utils.Round(7), currentRound)
} }
@ -346,7 +346,7 @@ func TestVoteMessageShallNotThrowErrorIfBlockNotYetExist(t *testing.T) {
err = engineV2.VoteHandler(blockchain, voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) assert.Nil(t, err)
currentRound, lockQuorumCert, highestQuorumCert, _, _ := engineV2.GetProperties() currentRound, lockQuorumCert, highestQuorumCert, _, _, _ := engineV2.GetPropertiesFaker()
// Still using the initlised value because we did not yet go to the next round // Still using the initlised value because we did not yet go to the next round
assert.Nil(t, lockQuorumCert) assert.Nil(t, lockQuorumCert)
assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round) assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round)
@ -364,7 +364,7 @@ func TestVoteMessageShallNotThrowErrorIfBlockNotYetExist(t *testing.T) {
err = engineV2.VoteHandler(blockchain, voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) assert.Nil(t, err)
currentRound, lockQuorumCert, highestQuorumCert, _, highestCommitBlock := engineV2.GetProperties() currentRound, lockQuorumCert, highestQuorumCert, _, _, highestCommitBlock := engineV2.GetPropertiesFaker()
// The lockQC shall be the parent's QC round number // The lockQC shall be the parent's QC round number
assert.Equal(t, utils.Round(5), lockQuorumCert.ProposedBlockInfo.Round) assert.Equal(t, utils.Round(5), lockQuorumCert.ProposedBlockInfo.Round)
// The highestQC proposedBlockInfo shall be the same as the one from its votes // The highestQC proposedBlockInfo shall be the same as the one from its votes
@ -375,25 +375,80 @@ func TestVoteMessageShallNotThrowErrorIfBlockNotYetExist(t *testing.T) {
assert.Equal(t, big.NewInt(904), highestCommitBlock.Number) assert.Equal(t, big.NewInt(904), highestCommitBlock.Number)
} }
func TestProcessVoteMsgFailIfVerifyBlockInfoFail(t *testing.T) {
blockchain, _, currentBlock, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, 0)
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
// Set round to 5
engineV2.SetNewRoundFaker(blockchain, utils.Round(5), false)
// Start with vote messages
blockInfo := &utils.BlockInfo{
Hash: currentBlock.ParentHash(),
Round: utils.Round(5),
Number: big.NewInt(905),
}
voteSigningHash := utils.VoteSigHash(blockInfo)
// Create two vote message which will not reach vote pool threshold
signedHash := SignHashByPK(acc1Key, voteSigningHash.Bytes())
voteMsg := &utils.Vote{
ProposedBlockInfo: blockInfo,
Signature: signedHash,
}
err := engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err)
currentRound, lockQuorumCert, highestQuorumCert, _, _, _ := engineV2.GetPropertiesFaker()
// initialised with nil and 0 round
assert.Nil(t, lockQuorumCert)
assert.Equal(t, utils.Round(0), highestQuorumCert.ProposedBlockInfo.Round)
assert.Equal(t, utils.Round(5), currentRound)
voteMsg = &utils.Vote{
ProposedBlockInfo: blockInfo,
Signature: SignHashByPK(acc2Key, voteSigningHash.Bytes()),
}
err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err)
currentRound, _, _, _, _, _ = engineV2.GetPropertiesFaker()
assert.Equal(t, utils.Round(5), currentRound)
// Create a vote message that should trigger vote pool hook
voteMsg = &utils.Vote{
ProposedBlockInfo: blockInfo,
Signature: SignHashByPK(acc3Key, voteSigningHash.Bytes()),
}
err = engineV2.VoteHandler(blockchain, voteMsg)
expectedError := fmt.Errorf("[VerifyBlockInfo] chain header number does not match for the received blockInfo at hash: %v", blockInfo.Hash.Hex())
assert.Equal(t, expectedError, err)
}
func TestVerifyVoteMsg(t *testing.T) { func TestVerifyVoteMsg(t *testing.T) {
blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 915, params.TestXDPoSMockChainConfig, 0) blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 915, params.TestXDPoSMockChainConfig, 0)
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
blockInfo := &utils.BlockInfo{ blockInfo := &utils.BlockInfo{
Hash: currentBlock.Hash(), Hash: currentBlock.Hash(),
Round: utils.Round(15), Round: utils.Round(14),
Number: big.NewInt(915), Number: big.NewInt(915),
} }
// Invalid vote msg // Valid message but disqualified as the round does not match
voteMsg := &utils.Vote{ voteMsg := &utils.Vote{
ProposedBlockInfo: blockInfo, ProposedBlockInfo: blockInfo,
Signature: []byte{1}, Signature: []byte{1},
} }
engineV2.SetNewRoundFaker(blockchain, utils.Round(15), false)
verified, err := engineV2.VerifyVoteMessage(blockchain, voteMsg) verified, err := engineV2.VerifyVoteMessage(blockchain, voteMsg)
assert.False(t, verified) assert.False(t, verified)
assert.NotNil(t, err) assert.Nil(t, err)
// Invalid vote message with wrong signature
engineV2.SetNewRoundFaker(blockchain, utils.Round(14), false)
verified, err = engineV2.VerifyVoteMessage(blockchain, voteMsg)
assert.False(t, verified)
assert.Equal(t, "Error while verifying message: invalid signature length", err.Error())
// Valid vote message from a master node // Valid vote message from a master node
signHash, _ := signFn(accounts.Account{Address: signer}, utils.VoteSigHash(blockInfo).Bytes()) signHash, _ := signFn(accounts.Account{Address: signer}, utils.VoteSigHash(blockInfo).Bytes())

View file

@ -145,6 +145,87 @@ func TestNotBoardcastInvalidVote(t *testing.T) {
} }
} }
func TestBoardcastButNotProcessDisqualifiedVotes(t *testing.T) {
tester := newTester()
handlerCounter := uint32(0)
broadcastCounter := uint32(0)
targetVotes := 0
tester.bfter.consensus.verifyVote = func(chain consensus.ChainReader, vote *utils.Vote) (bool, error) {
return false, nil // return false but with nil in error means the message is valid but disqualified
}
tester.bfter.consensus.voteHandler = func(chain consensus.ChainReader, vote *utils.Vote) error {
atomic.AddUint32(&handlerCounter, 1)
return nil
}
tester.bfter.broadcast.Vote = func(*utils.Vote) {
atomic.AddUint32(&broadcastCounter, 1)
}
vote := utils.Vote{ProposedBlockInfo: &utils.BlockInfo{}}
tester.bfter.Vote(&vote)
time.Sleep(50 * time.Millisecond)
if int(handlerCounter) != targetVotes || int(broadcastCounter) != 1 {
t.Fatalf("count mismatch: have %v on handler, %v on broadcast, want %v", handlerCounter, broadcastCounter, targetVotes)
}
}
func TestBoardcastButNotProcessDisqualifiedTimeout(t *testing.T) {
tester := newTester()
handlerCounter := uint32(0)
broadcastCounter := uint32(0)
targetTimeout := 0
tester.bfter.consensus.verifyTimeout = func(chain consensus.ChainReader, timeout *utils.Timeout) (bool, error) {
return false, nil // return false but with nil in error means the message is valid but disqualified
}
tester.bfter.consensus.timeoutHandler = func(chain consensus.ChainReader, timeout *utils.Timeout) error {
atomic.AddUint32(&handlerCounter, 1)
return nil
}
tester.bfter.broadcast.Timeout = func(*utils.Timeout) {
atomic.AddUint32(&broadcastCounter, 1)
}
timeout := utils.Timeout{}
tester.bfter.Timeout(&timeout)
time.Sleep(50 * time.Millisecond)
if int(handlerCounter) != targetTimeout || int(broadcastCounter) != 1 {
t.Fatalf("count mismatch: have %v on handler, %v on broadcast, want %v", handlerCounter, broadcastCounter, targetTimeout)
}
}
func TestBoardcastButNotProcessDisqualifiedSyncInfo(t *testing.T) {
tester := newTester()
handlerCounter := uint32(0)
broadcastCounter := uint32(0)
targetSyncInfo := 0
tester.bfter.consensus.verifySyncInfo = func(chain consensus.ChainReader, syncInfo *utils.SyncInfo) (bool, error) {
return false, nil // return false but with nil in error means the message is valid but disqualified
}
tester.bfter.consensus.syncInfoHandler = func(chain consensus.ChainReader, syncInfo *utils.SyncInfo) error {
atomic.AddUint32(&handlerCounter, 1)
return nil
}
tester.bfter.broadcast.SyncInfo = func(*utils.SyncInfo) {
atomic.AddUint32(&broadcastCounter, 1)
}
syncInfo := utils.SyncInfo{}
tester.bfter.SyncInfo(&syncInfo)
time.Sleep(50 * time.Millisecond)
if int(handlerCounter) != targetSyncInfo || int(broadcastCounter) != 1 {
t.Fatalf("count mismatch: have %v on handler, %v on broadcast, want %v", handlerCounter, broadcastCounter, targetSyncInfo)
}
}
// TODO: SyncInfo and Timeout Test, should be same as Vote. // TODO: SyncInfo and Timeout Test, should be same as Vote.
// Once all test on vote covered, then duplicate to others // Once all test on vote covered, then duplicate to others
@ -198,9 +279,7 @@ func TestTimeoutHandlerRoundNotEqual(t *testing.T) {
} }
} }
tester.bfter.broadcast.Timeout = func(*utils.Timeout) { tester.bfter.broadcast.Timeout = func(*utils.Timeout) {}
return
}
timeoutMsg := &utils.Timeout{} timeoutMsg := &utils.Timeout{}

View file

@ -1,8 +1,6 @@
package bft package bft
import ( import (
"fmt"
"github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
@ -40,7 +38,7 @@ type ConsensusFns struct {
verifyTimeout func(consensus.ChainReader, *utils.Timeout) (bool, error) verifyTimeout func(consensus.ChainReader, *utils.Timeout) (bool, error)
timeoutHandler func(consensus.ChainReader, *utils.Timeout) error timeoutHandler func(consensus.ChainReader, *utils.Timeout) error
verifySyncInfo func(consensus.ChainReader, *utils.SyncInfo) error verifySyncInfo func(consensus.ChainReader, *utils.SyncInfo) (bool, error)
syncInfoHandler func(consensus.ChainReader, *utils.SyncInfo) error syncInfoHandler func(consensus.ChainReader, *utils.SyncInfo) error
} }
@ -88,25 +86,25 @@ func (b *Bfter) Vote(vote *utils.Vote) error {
verified, err := b.consensus.verifyVote(b.blockChainReader, vote) verified, err := b.consensus.verifyVote(b.blockChainReader, vote)
if err != nil || !verified { if err != nil {
log.Error("Verify BFT Vote", "error", err, "verified", verified) log.Error("Verify BFT Vote", "error", err)
if !verified {
return fmt.Errorf("Fail to verify vote")
}
return err return err
} }
b.broadcastCh <- vote b.broadcastCh <- vote
err = b.consensus.voteHandler(b.blockChainReader, vote) if verified {
if err != nil { err = b.consensus.voteHandler(b.blockChainReader, vote)
if _, ok := err.(*utils.ErrIncomingMessageRoundTooFarFromCurrentRound); ok { if err != nil {
log.Warn("vote round not equal", "error", err, "vote", vote.Hash()) if _, ok := err.(*utils.ErrIncomingMessageRoundTooFarFromCurrentRound); ok {
log.Warn("vote round not equal", "error", err, "vote", vote.Hash())
return err
}
log.Error("handle BFT Vote", "error", err)
return err return err
} }
log.Error("handle BFT Vote", "error", err)
return err
} }
return nil return nil
} }
func (b *Bfter) Timeout(timeout *utils.Timeout) error { func (b *Bfter) Timeout(timeout *utils.Timeout) error {
@ -117,24 +115,23 @@ func (b *Bfter) Timeout(timeout *utils.Timeout) error {
} }
verified, err := b.consensus.verifyTimeout(b.blockChainReader, timeout) verified, err := b.consensus.verifyTimeout(b.blockChainReader, timeout)
if err != nil { if err != nil {
log.Error("Verify BFT Timeout", "error", err) log.Error("Verify BFT Timeout", "timeoutRound", timeout.Round, "timeoutGapNum", timeout.GapNumber, "error", err)
return err return err
} }
if !verified {
log.Warn("Timeout message failed to verify", "timeoutRound", timeout.Round, "timeoutGapNum", timeout.GapNumber)
return fmt.Errorf("Fail to verify the received timeout message")
}
b.broadcastCh <- timeout
err = b.consensus.timeoutHandler(b.blockChainReader, timeout) b.broadcastCh <- timeout
if err != nil { if verified {
if _, ok := err.(*utils.ErrIncomingMessageRoundNotEqualCurrentRound); ok { err = b.consensus.timeoutHandler(b.blockChainReader, timeout)
log.Warn("timeout round not equal", "error", err) if err != nil {
if _, ok := err.(*utils.ErrIncomingMessageRoundNotEqualCurrentRound); ok {
log.Warn("timeout round not equal", "error", err)
return err
}
log.Error("handle BFT Timeout", "error", err)
return err return err
} }
log.Error("handle BFT Timeout", "error", err)
return err
} }
return nil return nil
} }
func (b *Bfter) SyncInfo(syncInfo *utils.SyncInfo) error { func (b *Bfter) SyncInfo(syncInfo *utils.SyncInfo) error {
@ -143,18 +140,20 @@ func (b *Bfter) SyncInfo(syncInfo *utils.SyncInfo) error {
log.Trace("Discarded SyncInfo, known SyncInfo", "hash", syncInfo.Hash()) log.Trace("Discarded SyncInfo, known SyncInfo", "hash", syncInfo.Hash())
return nil return nil
} }
err := b.consensus.verifySyncInfo(b.blockChainReader, syncInfo) verified, err := b.consensus.verifySyncInfo(b.blockChainReader, syncInfo)
if err != nil { if err != nil {
log.Error("Verify BFT SyncInfo", "error", err) log.Error("Verify BFT SyncInfo", "error", err)
return err return err
} }
b.broadcastCh <- syncInfo b.broadcastCh <- syncInfo
// Process only if verified and qualified
err = b.consensus.syncInfoHandler(b.blockChainReader, syncInfo) if verified {
if err != nil { err = b.consensus.syncInfoHandler(b.blockChainReader, syncInfo)
log.Error("handle BFT SyncInfo", "error", err) if err != nil {
return err log.Error("handle BFT SyncInfo", "error", err)
return err
}
} }
return nil return nil
} }