diff --git a/consensus/XDPoS/XDPoS.go b/consensus/XDPoS/XDPoS.go index 24883b2860..84901cfe2e 100644 --- a/consensus/XDPoS/XDPoS.go +++ b/consensus/XDPoS/XDPoS.go @@ -149,8 +149,30 @@ func (x *XDPoS) VerifyHeader(chain consensus.ChainReader, header *types.Header, // method returns a quit channel to abort the operations and a results channel to // retrieve the async verifications (the order is that of the input slice). func (x *XDPoS) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, fullVerifies []bool) (chan<- struct{}, <-chan error) { - // TODO: (Hashlab) This funciton is a special case - return x.EngineV1.VerifyHeaders(chain, headers, fullVerifies) + abort := make(chan struct{}) + results := make(chan error, len(headers)) + + // Split the headers list into v1 and v2 buckets + var v1headers []*types.Header + var v2headers []*types.Header + + for _, header := range headers { + switch x.config.BlockConsensusVersion(header.Number) { + case params.ConsensusEngineVersion2: + v2headers = append(v2headers, header) + default: // Default "v1" + v1headers = append(v1headers, header) + } + } + + if v1headers != nil { + x.EngineV1.VerifyHeaders(chain, headers, fullVerifies, abort, results) + } + if v2headers != nil { + x.EngineV2.VerifyHeaders(chain, headers, fullVerifies, abort, results) + } + + return abort, results } // VerifyUncles implements consensus.Engine, always returning an error for any diff --git a/consensus/XDPoS/engines/engine_v1/engine.go b/consensus/XDPoS/engines/engine_v1/engine.go index 5ebc929ed7..731a863e8a 100644 --- a/consensus/XDPoS/engines/engine_v1/engine.go +++ b/consensus/XDPoS/engines/engine_v1/engine.go @@ -116,10 +116,7 @@ func (x *XDPoS_v1) VerifyHeader(chain consensus.ChainReader, header *types.Heade // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The // method returns a quit channel to abort the operations and a results channel to // retrieve the async verifications (the order is that of the input slice). -func (x *XDPoS_v1) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, fullVerifies []bool) (chan<- struct{}, <-chan error) { - abort := make(chan struct{}) - results := make(chan error, len(headers)) - +func (x *XDPoS_v1) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, fullVerifies []bool, abort <-chan struct{}, results chan<- error) { go func() { for i, header := range headers { err := x.verifyHeaderWithCache(chain, header, headers[:i], fullVerifies[i]) @@ -131,7 +128,6 @@ func (x *XDPoS_v1) VerifyHeaders(chain consensus.ChainReader, headers []*types.H } } }() - return abort, results } func (x *XDPoS_v1) verifyHeaderWithCache(chain consensus.ChainReader, header *types.Header, parents []*types.Header, fullVerify bool) error { diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index 5605ecb389..b1c8704caa 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -478,6 +478,19 @@ func (x *XDPoS_v2) VerifyHeader(chain consensus.ChainReader, header *types.Heade return nil } +// TODO: Yet to be implemented XIN-135 +func (x *XDPoS_v2) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, fullVerifies []bool, abort <-chan struct{}, results chan<- error) { + go func() { + for range headers { + select { + case <-abort: + return + case results <- nil: + } + } + }() +} + // Utils for test to get current Pool size func (x *XDPoS_v2) GetVotePoolSize(vote *utils.Vote) int { return x.votePool.Size(vote) @@ -556,8 +569,8 @@ func (x *XDPoS_v2) VoteHandler(chain consensus.ChainReader, voteMsg *utils.Vote) func (x *XDPoS_v2) voteHandler(chain consensus.ChainReader, voteMsg *utils.Vote) error { // 1. checkRoundNumber - if voteMsg.ProposedBlockInfo.Round != x.currentRound { - return &utils.ErrIncomingMessageRoundNotEqualCurrentRound{ + if (voteMsg.ProposedBlockInfo.Round != x.currentRound) && (voteMsg.ProposedBlockInfo.Round != x.currentRound+1) { + return &utils.ErrIncomingMessageRoundTooFarFromCurrentRound{ Type: "vote", IncomingRound: voteMsg.ProposedBlockInfo.Round, CurrentRound: x.currentRound, diff --git a/consensus/XDPoS/utils/errors.go b/consensus/XDPoS/utils/errors.go index 838dcfb19a..b638328511 100644 --- a/consensus/XDPoS/utils/errors.go +++ b/consensus/XDPoS/utils/errors.go @@ -88,3 +88,13 @@ type ErrIncomingMessageRoundNotEqualCurrentRound struct { func (e *ErrIncomingMessageRoundNotEqualCurrentRound) Error() string { return fmt.Sprintf("%s message round number: %v does not match currentRound: %v", e.Type, e.IncomingRound, e.CurrentRound) } + +type ErrIncomingMessageRoundTooFarFromCurrentRound struct { + Type string + IncomingRound Round + CurrentRound Round +} + +func (e *ErrIncomingMessageRoundTooFarFromCurrentRound) Error() string { + return fmt.Sprintf("%s message round number: %v is too far away from currentRound: %v", e.Type, e.IncomingRound, e.CurrentRound) +} diff --git a/consensus/tests/vote_test.go b/consensus/tests/vote_test.go index a6299ad557..c1374243e2 100644 --- a/consensus/tests/vote_test.go +++ b/consensus/tests/vote_test.go @@ -126,7 +126,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) { assert.Equal(t, big.NewInt(13), highestCommitBlock.Number) } -func TestThrowErrorIfVoteMsgRoundNotEqualToCurrentRound(t *testing.T) { +func TestThrowErrorIfVoteMsgRoundIsMoreThanOneRoundAwayFromCurrentRound(t *testing.T) { blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, 15, params.TestXDPoSMockChainConfigWithV2Engine, 0) engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 @@ -146,14 +146,18 @@ func TestThrowErrorIfVoteMsgRoundNotEqualToCurrentRound(t *testing.T) { // voteRound > currentRound err := engineV2.VoteHandler(blockchain, voteMsg) assert.NotNil(t, err) - assert.Equal(t, "vote message round number: 6 does not match currentRound: 7", err.Error()) + assert.Equal(t, "vote message round number: 6 is too far away from currentRound: 7", err.Error()) - // Set round to 5 + // Set round to 5, it's 1 round away, should not trigger failure engineV2.SetNewRoundFaker(utils.Round(5), false) err = engineV2.VoteHandler(blockchain, voteMsg) + assert.Nil(t, err) + + engineV2.SetNewRoundFaker(utils.Round(4), false) + err = engineV2.VoteHandler(blockchain, voteMsg) assert.NotNil(t, err) - // voteRound < currentRound - assert.Equal(t, "vote message round number: 6 does not match currentRound: 5", err.Error()) + assert.Equal(t, "vote message round number: 6 is too far away from currentRound: 4", err.Error()) + } func TestProcessVoteMsgThenTimeoutMsg(t *testing.T) { diff --git a/eth/bft/bft_handler.go b/eth/bft/bft_handler.go index 7a67893b80..25f8eba293 100644 --- a/eth/bft/bft_handler.go +++ b/eth/bft/bft_handler.go @@ -94,7 +94,7 @@ func (b *Bfter) Vote(vote *utils.Vote) error { err = b.consensus.voteHandler(b.blockChainReader, vote) if err != nil { - if _, ok := err.(*utils.ErrIncomingMessageRoundNotEqualCurrentRound); ok { + if _, ok := err.(*utils.ErrIncomingMessageRoundTooFarFromCurrentRound); ok { log.Warn("vote round not equal", "error", err, "vote", vote.Hash()) return err }