github.com/koko1123/flow-go-1@v0.29.6/consensus/hotstuff/eventhandler/event_handler_test.go (about)

     1  package eventhandler
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/rs/zerolog"
    10  	"github.com/rs/zerolog/log"
    11  	"github.com/stretchr/testify/mock"
    12  	"github.com/stretchr/testify/require"
    13  	"github.com/stretchr/testify/suite"
    14  
    15  	"github.com/koko1123/flow-go-1/consensus/hotstuff"
    16  	"github.com/koko1123/flow-go-1/consensus/hotstuff/mocks"
    17  	"github.com/koko1123/flow-go-1/consensus/hotstuff/model"
    18  	"github.com/koko1123/flow-go-1/consensus/hotstuff/notifications"
    19  	"github.com/koko1123/flow-go-1/consensus/hotstuff/pacemaker"
    20  	"github.com/koko1123/flow-go-1/consensus/hotstuff/pacemaker/timeout"
    21  	"github.com/koko1123/flow-go-1/model/flow"
    22  )
    23  
    24  const (
    25  	startRepTimeout        float64 = 400.0 // Milliseconds
    26  	minRepTimeout          float64 = 100.0 // Milliseconds
    27  	voteTimeoutFraction    float64 = 0.5   // multiplicative factor
    28  	multiplicativeIncrease float64 = 1.5   // multiplicative factor
    29  	multiplicativeDecrease float64 = 0.85  // multiplicative factor
    30  )
    31  
    32  // TestPaceMaker is a real pacemaker module with logging for view changes
    33  type TestPaceMaker struct {
    34  	hotstuff.PaceMaker
    35  	t require.TestingT
    36  }
    37  
    38  func NewTestPaceMaker(t require.TestingT, startView uint64, timeoutController *timeout.Controller, notifier hotstuff.Consumer) *TestPaceMaker {
    39  	p, err := pacemaker.New(startView, timeoutController, notifier)
    40  	if err != nil {
    41  		panic(err)
    42  	}
    43  	return &TestPaceMaker{p, t}
    44  }
    45  
    46  func (p *TestPaceMaker) UpdateCurViewWithQC(qc *flow.QuorumCertificate) (*model.NewViewEvent, bool) {
    47  	oldView := p.CurView()
    48  	newView, changed := p.PaceMaker.UpdateCurViewWithQC(qc)
    49  	log.Info().Msgf("pacemaker.UpdateCurViewWithQC old view: %v, new view: %v\n", oldView, p.CurView())
    50  	return newView, changed
    51  }
    52  
    53  func (p *TestPaceMaker) UpdateCurViewWithBlock(block *model.Block, isLeaderForNextView bool) (*model.NewViewEvent, bool) {
    54  	oldView := p.CurView()
    55  	newView, changed := p.PaceMaker.UpdateCurViewWithBlock(block, isLeaderForNextView)
    56  	log.Info().Msgf("pacemaker.UpdateCurViewWithBlock old view: %v, new view: %v\n", oldView, p.CurView())
    57  	return newView, changed
    58  }
    59  
    60  func (p *TestPaceMaker) OnTimeout() *model.NewViewEvent {
    61  	oldView := p.CurView()
    62  	newView := p.PaceMaker.OnTimeout()
    63  	log.Info().Msgf("pacemaker.OnTimeout old view: %v, new view: %v\n", oldView, p.CurView())
    64  	return newView
    65  }
    66  
    67  // using a real pacemaker for testing event handler
    68  func initPaceMaker(t require.TestingT, view uint64) hotstuff.PaceMaker {
    69  	notifier := &mocks.Consumer{}
    70  	tc, err := timeout.NewConfig(
    71  		time.Duration(startRepTimeout*1e6),
    72  		time.Duration(minRepTimeout*1e6),
    73  		voteTimeoutFraction,
    74  		multiplicativeIncrease,
    75  		multiplicativeDecrease,
    76  		0)
    77  	if err != nil {
    78  		t.FailNow()
    79  	}
    80  	pm := NewTestPaceMaker(t, view, timeout.NewController(tc), notifier)
    81  	notifier.On("OnStartingTimeout", mock.Anything).Return()
    82  	notifier.On("OnQcTriggeredViewChange", mock.Anything, mock.Anything).Return()
    83  	notifier.On("OnReachedTimeout", mock.Anything).Return()
    84  	pm.Start()
    85  	return pm
    86  }
    87  
    88  // VoteAggregator is a mock for testing eventhandler
    89  type VoteAggregator struct {
    90  	// if a blockID exists in qcs field, then a vote can be made into a QC
    91  	qcs map[flow.Identifier]*flow.QuorumCertificate
    92  	t   require.TestingT
    93  }
    94  
    95  func NewVoteAggregator(t require.TestingT) *VoteAggregator {
    96  	return &VoteAggregator{
    97  		qcs: make(map[flow.Identifier]*flow.QuorumCertificate),
    98  		t:   t,
    99  	}
   100  }
   101  
   102  func (v *VoteAggregator) StoreVoteAndBuildQC(vote *model.Vote, block *model.Block) (*flow.QuorumCertificate, bool, error) {
   103  	qc, ok := v.qcs[block.BlockID]
   104  	log.Info().Msgf("voteaggregator.StoreVoteAndBuildQC, qc built: %v, for view: %x, blockID: %v\n", ok, block.View, block.BlockID)
   105  
   106  	return qc, ok, nil
   107  }
   108  
   109  func (v *VoteAggregator) StorePendingVote(vote *model.Vote) (bool, error) {
   110  	return false, nil
   111  }
   112  
   113  func (v *VoteAggregator) StoreProposerVote(vote *model.Vote) bool {
   114  	return true
   115  }
   116  
   117  func (v *VoteAggregator) BuildQCOnReceivedBlock(block *model.Block) (*flow.QuorumCertificate, bool, error) {
   118  	qc, ok := v.qcs[block.BlockID]
   119  	log.Info().Msgf("voteaggregator.BuildQCOnReceivedBlock, qc built: %v, for view: %x, blockID: %v\n", ok, block.View, block.BlockID)
   120  
   121  	return qc, ok, nil
   122  }
   123  
   124  func (v *VoteAggregator) PruneByView(view uint64) {
   125  	log.Info().Msgf("pruned at view:%v\n", view)
   126  }
   127  
   128  type Committee struct {
   129  	mocks.Committee
   130  	// to mock I'm the leader of a certain view, add the view into the keys of leaders field
   131  	leaders map[uint64]struct{}
   132  }
   133  
   134  func NewCommittee() *Committee {
   135  	return &Committee{
   136  		leaders: make(map[uint64]struct{}),
   137  	}
   138  }
   139  
   140  func (c *Committee) LeaderForView(view uint64) (flow.Identifier, error) {
   141  	_, isLeader := c.leaders[view]
   142  	if isLeader {
   143  		return flow.Identifier{0x01}, nil
   144  	}
   145  	return flow.Identifier{0x00}, nil
   146  }
   147  
   148  func (c *Committee) Self() flow.Identifier {
   149  	return flow.Identifier{0x01}
   150  }
   151  
   152  // The Voter mock will not vote for any block unless the block's ID exists in votable field's key
   153  type Voter struct {
   154  	votable       map[flow.Identifier]struct{}
   155  	lastVotedView uint64
   156  	t             require.TestingT
   157  }
   158  
   159  func NewVoter(t require.TestingT, lastVotedView uint64) *Voter {
   160  	return &Voter{
   161  		votable:       make(map[flow.Identifier]struct{}),
   162  		lastVotedView: lastVotedView,
   163  		t:             t,
   164  	}
   165  }
   166  
   167  // voter will not vote for any block, unless the blockID exists in votable map
   168  func (v *Voter) ProduceVoteIfVotable(block *model.Block, curView uint64) (*model.Vote, error) {
   169  	_, ok := v.votable[block.BlockID]
   170  	if !ok {
   171  		return nil, model.NoVoteError{Msg: "block not found"}
   172  	}
   173  	return createVote(block), nil
   174  }
   175  
   176  // Forks mock allows to customize the Add QC and AddBlock function by specifying the addQC and addBlock callbacks
   177  type Forks struct {
   178  	mocks.Forks
   179  	// blocks stores all the blocks that have been added to the forks
   180  	blocks    map[flow.Identifier]*model.Block
   181  	finalized uint64
   182  	t         require.TestingT
   183  	qc        *flow.QuorumCertificate
   184  	// addQC is to customize the logic to change finalized view
   185  	addQC func(qc *flow.QuorumCertificate) error
   186  	// addBlock is to customize the logic to change finalized view
   187  	addBlock func(block *model.Block) error
   188  }
   189  
   190  func NewForks(t require.TestingT, finalized uint64) *Forks {
   191  	f := &Forks{
   192  		blocks:    make(map[flow.Identifier]*model.Block),
   193  		finalized: finalized,
   194  		t:         t,
   195  	}
   196  
   197  	f.addQC = func(qc *flow.QuorumCertificate) error {
   198  		if f.qc == nil || qc.View > f.qc.View {
   199  			f.qc = qc
   200  		}
   201  		return nil
   202  	}
   203  	f.addBlock = func(block *model.Block) error {
   204  		f.blocks[block.BlockID] = block
   205  		if block.QC == nil {
   206  			panic(fmt.Sprintf("block has no QC: %v", block.View))
   207  		}
   208  		_ = f.addQC(block.QC)
   209  		return nil
   210  	}
   211  
   212  	qc := createQC(createBlock(finalized))
   213  	_ = f.addQC(qc)
   214  
   215  	return f
   216  }
   217  
   218  func (f *Forks) AddBlock(block *model.Block) error {
   219  	log.Info().Msgf("forks.AddBlock received Block for view: %v, qc: %v\n", block.View, block.QC.View)
   220  	return f.addBlock(block)
   221  }
   222  
   223  func (f *Forks) AddQC(qc *flow.QuorumCertificate) error {
   224  	log.Info().Msgf("forks.AddQC received QC for view: %v\n", qc.View)
   225  	return f.addQC(qc)
   226  }
   227  
   228  func (f *Forks) FinalizedView() uint64 {
   229  	return f.finalized
   230  }
   231  
   232  func (f *Forks) GetBlock(blockID flow.Identifier) (*model.Block, bool) {
   233  	b, ok := f.blocks[blockID]
   234  	var view uint64
   235  	if ok {
   236  		view = b.View
   237  	}
   238  	log.Info().Msgf("forks.GetBlock found: %v, view: %v\n", ok, view)
   239  	return b, ok
   240  }
   241  
   242  func (f *Forks) GetBlocksForView(view uint64) []*model.Block {
   243  	blocks := make([]*model.Block, 0)
   244  	for _, b := range f.blocks {
   245  		if b.View == view {
   246  			blocks = append(blocks, b)
   247  		}
   248  	}
   249  	log.Info().Msgf("forks.GetBlocksForView found %v block(s) for view %v\n", len(blocks), view)
   250  	return blocks
   251  }
   252  
   253  func (f *Forks) MakeForkChoice(curView uint64) (*flow.QuorumCertificate, *model.Block, error) {
   254  	if f.qc == nil {
   255  		log.Fatal().Msgf("cannot make fork choice for curview: %v", curView)
   256  	}
   257  
   258  	block, ok := f.blocks[f.qc.BlockID]
   259  	if !ok {
   260  		return nil, nil, fmt.Errorf("cannot block %V for fork choice qc", f.qc.BlockID)
   261  	}
   262  	log.Info().Msgf("forks.MakeForkChoice for view: %v, qc view: %v\n", curView, f.qc.View)
   263  	return f.qc, block, nil
   264  }
   265  
   266  // BlockProducer mock will always make a valid block
   267  type BlockProducer struct{}
   268  
   269  func (b *BlockProducer) MakeBlockProposal(qc *flow.QuorumCertificate, view uint64) (*model.Proposal, error) {
   270  	return createProposal(view, qc.View), nil
   271  }
   272  
   273  // BlacklistValidator is Validator mock that consider all proposals are valid unless the proposal's BlockID exists
   274  // in the invalidProposals key or unverifiable key
   275  type BlacklistValidator struct {
   276  	mocks.Validator
   277  	invalidProposals map[flow.Identifier]struct{}
   278  	unverifiable     map[flow.Identifier]struct{}
   279  	t                require.TestingT
   280  }
   281  
   282  func NewBlacklistValidator(t require.TestingT) *BlacklistValidator {
   283  	return &BlacklistValidator{
   284  		invalidProposals: make(map[flow.Identifier]struct{}),
   285  		unverifiable:     make(map[flow.Identifier]struct{}),
   286  		t:                t,
   287  	}
   288  }
   289  
   290  func (v *BlacklistValidator) ValidateProposal(proposal *model.Proposal) error {
   291  	// check if is invalid
   292  	_, ok := v.invalidProposals[proposal.Block.BlockID]
   293  	if ok {
   294  		log.Info().Msgf("invalid proposal: %v\n", proposal.Block.View)
   295  		return model.InvalidBlockError{
   296  			BlockID: proposal.Block.BlockID,
   297  			View:    proposal.Block.View,
   298  			Err:     fmt.Errorf("some error"),
   299  		}
   300  	}
   301  
   302  	// check if is unverifiable
   303  	_, ok = v.unverifiable[proposal.Block.BlockID]
   304  	if ok {
   305  		log.Info().Msgf("unverifiable proposal: %v\n", proposal.Block.View)
   306  		return model.ErrUnverifiableBlock
   307  	}
   308  
   309  	return nil
   310  }
   311  
   312  func TestEventHandler(t *testing.T) {
   313  	suite.Run(t, new(EventHandlerSuite))
   314  }
   315  
   316  // EventHandlerSuite contains mocked state for testing event handler under different scenarios.
   317  type EventHandlerSuite struct {
   318  	suite.Suite
   319  
   320  	eventhandler *EventHandler
   321  
   322  	paceMaker      hotstuff.PaceMaker
   323  	forks          *Forks
   324  	persist        *mocks.Persister
   325  	blockProducer  *BlockProducer
   326  	communicator   *mocks.Communicator
   327  	committee      *Committee
   328  	voteAggregator *mocks.VoteAggregator
   329  	voter          *Voter
   330  	validator      *BlacklistValidator
   331  	notifier       hotstuff.Consumer
   332  
   333  	initView    uint64
   334  	endView     uint64
   335  	votingBlock *model.Block
   336  	qc          *flow.QuorumCertificate
   337  	newview     *model.NewViewEvent
   338  }
   339  
   340  func (es *EventHandlerSuite) SetupTest() {
   341  	finalized, curView := uint64(3), uint64(6)
   342  
   343  	es.paceMaker = initPaceMaker(es.T(), curView)
   344  	es.forks = NewForks(es.T(), finalized)
   345  	es.persist = &mocks.Persister{}
   346  	es.persist.On("PutStarted", mock.Anything).Return(nil)
   347  	es.blockProducer = &BlockProducer{}
   348  	es.communicator = &mocks.Communicator{}
   349  	es.communicator.On("BroadcastProposalWithDelay", mock.Anything, mock.Anything).Return(nil)
   350  	es.communicator.On("SendVote", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
   351  	es.committee = NewCommittee()
   352  	es.voteAggregator = &mocks.VoteAggregator{}
   353  	es.voter = NewVoter(es.T(), finalized)
   354  	es.validator = NewBlacklistValidator(es.T())
   355  	es.notifier = &notifications.NoopConsumer{}
   356  
   357  	eventhandler, err := NewEventHandler(
   358  		zerolog.New(os.Stderr),
   359  		es.paceMaker,
   360  		es.blockProducer,
   361  		es.forks,
   362  		es.persist,
   363  		es.communicator,
   364  		es.committee,
   365  		es.voteAggregator,
   366  		es.voter,
   367  		es.validator,
   368  		es.notifier)
   369  	require.NoError(es.T(), err)
   370  
   371  	es.eventhandler = eventhandler
   372  
   373  	es.initView = curView
   374  	es.endView = curView
   375  	// voting block is a block for the current view, which will trigger view change
   376  	es.votingBlock = createBlockWithQC(es.paceMaker.CurView(), es.paceMaker.CurView()-1)
   377  	es.qc = &flow.QuorumCertificate{
   378  		BlockID:       es.votingBlock.BlockID,
   379  		View:          es.votingBlock.View,
   380  		SignerIndices: nil,
   381  		SigData:       nil,
   382  	}
   383  	es.newview = &model.NewViewEvent{
   384  		View: es.votingBlock.View + 1, // the vote for the voting blocks will trigger a view change to the next view
   385  	}
   386  }
   387  
   388  func (es *EventHandlerSuite) markInvalidProposal(blockID flow.Identifier) {
   389  	es.validator.invalidProposals[blockID] = struct{}{}
   390  }
   391  
   392  // a QC for current view triggered view change
   393  func (es *EventHandlerSuite) TestQCBuiltViewChanged() {
   394  	// voting block exists
   395  	es.forks.blocks[es.votingBlock.BlockID] = es.votingBlock
   396  
   397  	// a qc is built
   398  	qc := createQC(es.votingBlock)
   399  
   400  	// new qc is added to forks
   401  	// view changed
   402  	// I'm not the next leader
   403  	// haven't received block for next view
   404  	// goes to the new view
   405  	es.endView++
   406  	// not the leader of the newview
   407  	// don't have block for the newview
   408  	// over
   409  
   410  	err := es.eventhandler.OnQCConstructed(qc)
   411  	require.NoError(es.T(), err, "if a vote can trigger a QC to be built,"+
   412  		"and the QC triggered a view change, then start new view")
   413  	require.Equal(es.T(), es.endView, es.paceMaker.CurView(), "incorrect view change")
   414  }
   415  
   416  // a QC for future view triggered view change
   417  func (es *EventHandlerSuite) TestQCBuiltFutureViewChanged() {
   418  	// voting block exists
   419  	curView := es.paceMaker.CurView()
   420  
   421  	// b1 is for current view
   422  	// b2 and b3 is for future view, but branched out from the same parent as b1
   423  	b1 := createBlockWithQC(curView, curView-1)
   424  	b2 := createBlockWithQC(curView+1, curView-1)
   425  	b3 := createBlockWithQC(curView+2, curView-1)
   426  
   427  	// a qc is built
   428  	// qc3 is for future view
   429  	// qc2 is an older than qc3
   430  	// since vote aggregator can concurrently process votes and build qcs,
   431  	// we prepare qcs at different view to be processed, and verify the view change.
   432  	qc1 := createQC(b1)
   433  	qc2 := createQC(b2)
   434  	qc3 := createQC(b3)
   435  
   436  	// all three blocks are known
   437  	es.forks.blocks[b1.BlockID] = b1
   438  	es.forks.blocks[b2.BlockID] = b2
   439  	es.forks.blocks[b3.BlockID] = b3
   440  
   441  	// test that qc for future view should trigger view change
   442  	err := es.eventhandler.OnQCConstructed(qc3)
   443  	endView := b3.View + 1 // next view
   444  	require.NoError(es.T(), err, "if a vote can trigger a QC to be built,"+
   445  		"and the QC triggered a view change, then start new view")
   446  	require.Equal(es.T(), endView, es.paceMaker.CurView(), "incorrect view change")
   447  
   448  	// the same qc would not trigger view change
   449  	err = es.eventhandler.OnQCConstructed(qc3)
   450  	endView = b3.View + 1 // next view
   451  	require.NoError(es.T(), err, "same qc should not trigger view change")
   452  	require.Equal(es.T(), endView, es.paceMaker.CurView(), "incorrect view change")
   453  
   454  	// old QCs won't trigger view change
   455  	err = es.eventhandler.OnQCConstructed(qc2)
   456  	require.NoError(es.T(), err)
   457  	require.Equal(es.T(), endView, es.paceMaker.CurView(), "incorrect view change")
   458  
   459  	err = es.eventhandler.OnQCConstructed(qc1)
   460  	require.NoError(es.T(), err)
   461  	require.Equal(es.T(), endView, es.paceMaker.CurView(), "incorrect view change")
   462  }
   463  
   464  // in the newview, I'm not the leader, and I have the cur block,
   465  // and the block is not a safe node, and I'm the next leader, and no qc built for this block.
   466  func (es *EventHandlerSuite) TestInNewView_NotLeader_HasBlock_NoVote_IsNextLeader_NoQC() {
   467  	// voting block exists
   468  	es.forks.blocks[es.votingBlock.BlockID] = es.votingBlock
   469  	// a qc is built
   470  	qc := createQC(es.votingBlock)
   471  	// viewchanged
   472  	es.endView++
   473  	// not leader for newview
   474  
   475  	// has block for newview
   476  	newviewblock := createBlockWithQC(es.newview.View, es.newview.View-1)
   477  	es.forks.blocks[newviewblock.BlockID] = newviewblock
   478  
   479  	// I'm the next leader
   480  	es.committee.leaders[es.newview.View+1] = struct{}{}
   481  
   482  	// no QC for the new view
   483  	err := es.eventhandler.OnQCConstructed(qc)
   484  	require.NoError(es.T(), err)
   485  	require.Equal(es.T(), es.endView, es.paceMaker.CurView(), "incorrect view change")
   486  }
   487  
   488  // TestInNewView_NotLeader_HasBlock_NoVote_IsNextLeader_QCBuilt_NoViewChange doesn't exist
   489  // in the newview, I'm not the leader, and I have the cur block,
   490  // and the block is not a safe node, and I'm the next leader, and a qc is built for this block,
   491  // and the qc triggered view change.
   492  func (es *EventHandlerSuite) TestInNewView_NotLeader_HasBlock_NoVote_IsNextLeader_QCBuilt_ViewChanged() {
   493  	// voting block exists
   494  	es.forks.blocks[es.votingBlock.BlockID] = es.votingBlock
   495  	// a qc is built
   496  	qc := createQC(es.votingBlock)
   497  	// viewchanged
   498  	es.endView++
   499  	// not leader for newview
   500  
   501  	// has block for newview
   502  	newviewblock := createBlockWithQC(es.newview.View, es.newview.View-1)
   503  	es.forks.blocks[newviewblock.BlockID] = newviewblock
   504  
   505  	// not to vote for the new view block
   506  
   507  	// I'm the next leader
   508  	es.committee.leaders[es.newview.View+1] = struct{}{}
   509  
   510  	// qc built for the new view block
   511  	nextQC := createQC(newviewblock)
   512  	// view change by this qc
   513  	es.endView++
   514  
   515  	err := es.eventhandler.OnQCConstructed(qc)
   516  	require.NoError(es.T(), err)
   517  
   518  	// no broadcast shouldn't be made with first qc because we are not a leader
   519  	es.communicator.AssertNotCalled(es.T(), "BroadcastProposalWithDelay")
   520  
   521  	err = es.eventhandler.OnQCConstructed(nextQC)
   522  	require.NoError(es.T(), err)
   523  
   524  	lastCall := es.communicator.Calls[len(es.communicator.Calls)-1]
   525  	// the last call is BroadcastProposal
   526  	require.Equal(es.T(), "BroadcastProposalWithDelay", lastCall.Method)
   527  	header, ok := lastCall.Arguments[0].(*flow.Header)
   528  	require.True(es.T(), ok)
   529  	// it should broadcast a header as the same as endView
   530  	require.Equal(es.T(), es.endView, header.View)
   531  }
   532  
   533  // in the newview, I'm not the leader, and I have the cur block,
   534  // and the block is a safe node to vote, and I'm the next leader, and no qc is built for this block.
   535  func (es *EventHandlerSuite) TestInNewView_NotLeader_HasBlock_NotSafeNode_IsNextLeader_Voted_NoQC() {
   536  	// voting block exists
   537  	es.forks.blocks[es.votingBlock.BlockID] = es.votingBlock
   538  	// a qc is built
   539  	qc := createQC(es.votingBlock)
   540  	// viewchanged by new qc
   541  	es.endView++
   542  	// not leader for newview
   543  
   544  	// has block for newview
   545  	newviewblock := createBlockWithQC(es.newview.View, es.newview.View-1)
   546  	es.forks.blocks[newviewblock.BlockID] = newviewblock
   547  
   548  	// not to vote for the new view block
   549  
   550  	// I'm the next leader
   551  	es.committee.leaders[es.newview.View+1] = struct{}{}
   552  
   553  	// no qc for the newview block
   554  
   555  	// should not trigger view change
   556  	err := es.eventhandler.OnQCConstructed(qc)
   557  	require.NoError(es.T(), err)
   558  	require.Equal(es.T(), es.endView, es.paceMaker.CurView(), "incorrect view change")
   559  }
   560  
   561  // in the newview, I'm not the leader, and I have the cur block,
   562  // and the block is not a safe node to vote, and I'm not the next leader
   563  func (es *EventHandlerSuite) TestInNewView_NotLeader_HasBlock_NotSafeNode_NotNextLeader() {
   564  	// voting block exists
   565  	es.forks.blocks[es.votingBlock.BlockID] = es.votingBlock
   566  	// a qc is built
   567  	qc := createQC(es.votingBlock)
   568  	// viewchanged by new qc
   569  	es.endView++
   570  
   571  	// view changed to newview
   572  	// I'm not the leader for newview
   573  
   574  	// have received block for cur view
   575  	newviewblock := createBlockWithQC(es.newview.View, es.newview.View-1)
   576  	es.forks.blocks[newviewblock.BlockID] = newviewblock
   577  
   578  	// I'm not the next leader
   579  	// no vote for this block
   580  	// goes to the next view
   581  	es.endView++
   582  	// not leader for next view
   583  
   584  	err := es.eventhandler.OnQCConstructed(qc)
   585  	require.NoError(es.T(), err, "if a vote can trigger a QC to be built,"+
   586  		"and the QC triggered a view change, then start new view")
   587  	require.Equal(es.T(), es.endView, es.paceMaker.CurView(), "incorrect view change")
   588  }
   589  
   590  // receiving an invalid proposal should not trigger view change
   591  func (es *EventHandlerSuite) TestOnReceiveProposal_InvalidProposal_NoViewChange() {
   592  	proposal := createProposal(es.initView, es.initView-1)
   593  	// invalid proposal
   594  	es.markInvalidProposal(proposal.Block.BlockID)
   595  	es.voteAggregator.On("InvalidBlock", proposal).Return(nil).Once()
   596  
   597  	err := es.eventhandler.OnReceiveProposal(proposal)
   598  	require.NoError(es.T(), err)
   599  	require.Equal(es.T(), es.initView, es.paceMaker.CurView(), "incorrect view change")
   600  }
   601  
   602  // received a valid proposal that has older view, and cannot build qc from votes for this block,
   603  // the proposal's QC didn't trigger view change
   604  func (es *EventHandlerSuite) TestOnReceiveProposal_OlderThanCurView_CannotBuildQCFromVotes_NoViewChange() {
   605  	proposal := createProposal(es.initView-1, es.initView-2)
   606  	es.voteAggregator.On("AddBlock", proposal).Return(nil).Once()
   607  
   608  	// can not build qc from votes for block
   609  	// should not trigger view change
   610  	err := es.eventhandler.OnReceiveProposal(proposal)
   611  	require.NoError(es.T(), err)
   612  	require.Equal(es.T(), es.endView, es.paceMaker.CurView(), "incorrect view change")
   613  	es.voteAggregator.AssertCalled(es.T(), "AddBlock", proposal)
   614  }
   615  
   616  // received a valid proposal that has older view, and can built a qc from votes for this block,
   617  // the proposal's QC didn't trigger view change
   618  func (es *EventHandlerSuite) TestOnReceiveProposal_OlderThanCurView_CanBuildQCFromVotes_NoViewChange() {
   619  	proposal := createProposal(es.initView-1, es.initView-2)
   620  	es.voteAggregator.On("AddBlock", proposal).Return(nil).Once()
   621  
   622  	//should not trigger view change
   623  	err := es.eventhandler.OnReceiveProposal(proposal)
   624  	require.NoError(es.T(), err)
   625  	require.Equal(es.T(), es.endView, es.paceMaker.CurView(), "incorrect view change")
   626  	es.voteAggregator.AssertCalled(es.T(), "AddBlock", proposal)
   627  }
   628  
   629  // received a valid proposal that has newer view, and cannot build qc from votes for this block,
   630  // the proposal's QC triggered view change
   631  func (es *EventHandlerSuite) TestOnReceiveProposal_NewerThanCurView_CannotBuildQCFromVotes_ViewChange() {
   632  	proposal := createProposal(es.initView+1, es.initView)
   633  	es.voteAggregator.On("AddBlock", proposal).Return(nil).Once()
   634  
   635  	// can not build qc from votes for block
   636  	// block 7 triggered view change
   637  	es.endView++
   638  
   639  	// not leader of view 7, go to view 8
   640  	es.endView++
   641  	err := es.eventhandler.OnReceiveProposal(proposal)
   642  	require.NoError(es.T(), err)
   643  	require.Equal(es.T(), es.endView, es.paceMaker.CurView(), "incorrect view change")
   644  	es.voteAggregator.AssertCalled(es.T(), "AddBlock", proposal)
   645  }
   646  
   647  // received a valid proposal that has newer view, and can build qc from votes for this block,
   648  // the proposal's QC triggered view change
   649  func (es *EventHandlerSuite) TestOnReceiveProposal_NewerThanCurView_CanBuildQCFromVotes_ViewChange() {
   650  	proposal := createProposal(es.initView+1, es.initView)
   651  	es.voteAggregator.On("AddBlock", proposal).Return(nil).Once()
   652  
   653  	es.forks.blocks[proposal.Block.BlockID] = proposal.Block
   654  
   655  	// trigged view change
   656  	es.endView++
   657  	// the proposal is for next view, has block for next view, no vote, trigger view change
   658  	es.endView++
   659  
   660  	err := es.eventhandler.OnReceiveProposal(proposal)
   661  	require.NoError(es.T(), err)
   662  	require.Equal(es.T(), es.endView, es.paceMaker.CurView(), "incorrect view change")
   663  	es.voteAggregator.AssertCalled(es.T(), "AddBlock", proposal)
   664  }
   665  
   666  // received a valid proposal whose QC that has newer view, and cannot build qc from votes for this block,
   667  // the proposal's QC triggered view change
   668  func (es *EventHandlerSuite) TestOnReceiveProposal_QCNewerThanCurView_CannotBuildQCFromVotes_ViewChanged() {
   669  	proposal := createProposal(es.initView+2, es.initView+1)
   670  	es.voteAggregator.On("AddBlock", proposal).Return(nil).Once()
   671  
   672  	// can not build qc from votes for block
   673  	// block 8 triggered view change
   674  	es.endView = es.endView + 2
   675  
   676  	// not leader of view 8, go to view 9
   677  	es.endView++
   678  	err := es.eventhandler.OnReceiveProposal(proposal)
   679  	require.NoError(es.T(), err)
   680  	require.Equal(es.T(), es.endView, es.paceMaker.CurView(), "incorrect view change")
   681  	require.Contains(es.T(), es.forks.blocks, proposal.Block.BlockID, "proposal block should be stored")
   682  	es.voteAggregator.AssertCalled(es.T(), "AddBlock", proposal)
   683  }
   684  
   685  // received a valid proposal for cur view, but not a safe node to vote, and I'm the next leader,
   686  // no qc for the block
   687  func (es *EventHandlerSuite) TestOnReceiveProposal_ForCurView_NoVote_IsNextLeader_NoQC() {
   688  	proposal := createProposal(es.initView, es.initView-1)
   689  	es.voteAggregator.On("AddBlock", proposal).Return(nil).Once()
   690  
   691  	// I'm the next leader
   692  	es.committee.leaders[es.initView+1] = struct{}{}
   693  	// no qc can be built for this block
   694  	err := es.eventhandler.OnReceiveProposal(proposal)
   695  	require.NoError(es.T(), err)
   696  	require.Equal(es.T(), es.endView, es.paceMaker.CurView(), "incorrect view change")
   697  	es.voteAggregator.AssertCalled(es.T(), "AddBlock", proposal)
   698  }
   699  
   700  // received a valid proposal for cur view, but not a safe node to vote, and I'm the next leader,
   701  // a qc can be built for the block, triggered view change
   702  func (es *EventHandlerSuite) TestOnReceiveProposal_ForCurView_NoVote_IsNextLeader_QCBuilt_ViewChange() {
   703  	proposal := createProposal(es.initView, es.initView-1)
   704  	qc := createQC(proposal.Block)
   705  	es.voteAggregator.On("AddBlock", proposal).Return(nil).Once()
   706  	// I'm the next leader
   707  	es.committee.leaders[es.initView+1] = struct{}{}
   708  	// qc triggered view change
   709  	es.endView++
   710  	// I'm the leader of cur view (7)
   711  	// I'm not the leader of next view (8), trigger view change
   712  
   713  	err := es.eventhandler.OnReceiveProposal(proposal)
   714  	require.NoError(es.T(), err)
   715  
   716  	// after receiving proposal build QC and deliver it to event handler
   717  	err = es.eventhandler.OnQCConstructed(qc)
   718  	require.NoError(es.T(), err)
   719  
   720  	lastCall := es.communicator.Calls[len(es.communicator.Calls)-1]
   721  	// the last call is BroadcastProposal
   722  	require.Equal(es.T(), "BroadcastProposalWithDelay", lastCall.Method)
   723  	header, ok := lastCall.Arguments[0].(*flow.Header)
   724  	require.True(es.T(), ok)
   725  	// it should broadcast a header as the same as endView
   726  	require.Equal(es.T(), es.endView, header.View)
   727  
   728  	require.Equal(es.T(), es.endView, es.paceMaker.CurView(), "incorrect view change")
   729  	es.voteAggregator.AssertCalled(es.T(), "AddBlock", proposal)
   730  }
   731  
   732  // received a unverifiable proposal for future view, no view change
   733  func (es *EventHandlerSuite) TestOnReceiveProposal_Unverifiable() {
   734  	// qc.View is below the finalized view
   735  	proposal := createProposal(es.forks.finalized+2, es.forks.finalized-1)
   736  
   737  	es.voteAggregator.On("AddBlock", proposal).Return(nil).Once()
   738  
   739  	// proposal is unverifiable
   740  	es.validator.unverifiable[proposal.Block.BlockID] = struct{}{}
   741  
   742  	err := es.eventhandler.OnReceiveProposal(proposal)
   743  	require.NoError(es.T(), err)
   744  	require.Equal(es.T(), es.endView, es.paceMaker.CurView(), "incorrect view change")
   745  	es.voteAggregator.AssertCalled(es.T(), "AddBlock", proposal)
   746  }
   747  
   748  func (es *EventHandlerSuite) TestOnTimeout() {
   749  	err := es.eventhandler.OnLocalTimeout()
   750  	// timeout will trigger viewchange
   751  	es.endView++
   752  	require.NoError(es.T(), err)
   753  	require.Equal(es.T(), es.endView, es.paceMaker.CurView(), "incorrect view change")
   754  }
   755  
   756  func (es *EventHandlerSuite) Test100Timeout() {
   757  	for i := 0; i < 100; i++ {
   758  		err := es.eventhandler.OnLocalTimeout()
   759  		es.endView++
   760  		require.NoError(es.T(), err)
   761  	}
   762  	require.Equal(es.T(), es.endView, es.paceMaker.CurView(), "incorrect view change")
   763  }
   764  
   765  // a leader builds 100 blocks one after another
   766  func (es *EventHandlerSuite) TestLeaderBuild100Blocks() {
   767  	// I'm the leader for the first view
   768  	es.committee.leaders[es.initView] = struct{}{}
   769  
   770  	totalView := 100
   771  	for i := 0; i < totalView; i++ {
   772  		// I'm the leader for 100 views
   773  		// I'm the next leader
   774  		es.committee.leaders[es.initView+uint64(i+1)] = struct{}{}
   775  		// I can build qc for all 100 views
   776  		proposal := createProposal(es.initView+uint64(i), es.initView+uint64(i)-1)
   777  		qc := createQC(proposal.Block)
   778  
   779  		es.voteAggregator.On("AddBlock", proposal).Return(nil).Once()
   780  		es.voteAggregator.On("AddVote", proposal.ProposerVote()).Return(nil).Once()
   781  
   782  		es.voter.votable[proposal.Block.BlockID] = struct{}{}
   783  		// should trigger 100 view change
   784  		es.endView++
   785  
   786  		err := es.eventhandler.OnReceiveProposal(proposal)
   787  		require.NoError(es.T(), err)
   788  		err = es.eventhandler.OnQCConstructed(qc)
   789  		require.NoError(es.T(), err)
   790  
   791  		lastCall := es.communicator.Calls[len(es.communicator.Calls)-1]
   792  		require.Equal(es.T(), "BroadcastProposalWithDelay", lastCall.Method)
   793  		header, ok := lastCall.Arguments[0].(*flow.Header)
   794  		require.True(es.T(), ok)
   795  		require.Equal(es.T(), proposal.Block.View+1, header.View)
   796  	}
   797  
   798  	require.Equal(es.T(), es.endView, es.paceMaker.CurView(), "incorrect view change")
   799  	require.Equal(es.T(), totalView, len(es.forks.blocks))
   800  	es.voteAggregator.AssertExpectations(es.T())
   801  }
   802  
   803  // a follower receives 100 blocks
   804  func (es *EventHandlerSuite) TestFollowerFollows100Blocks() {
   805  	for i := 0; i < 100; i++ {
   806  		// create each proposal as if they are created by some leader
   807  		proposal := createProposal(es.initView+uint64(i), es.initView+uint64(i)-1)
   808  		es.voteAggregator.On("AddBlock", proposal).Return(nil).Once()
   809  		// as a follower, I receive these propsals
   810  		err := es.eventhandler.OnReceiveProposal(proposal)
   811  		require.NoError(es.T(), err)
   812  		es.endView++
   813  	}
   814  	require.Equal(es.T(), es.endView, es.paceMaker.CurView(), "incorrect view change")
   815  	require.Equal(es.T(), 100, len(es.forks.blocks))
   816  	es.voteAggregator.AssertExpectations(es.T())
   817  }
   818  
   819  // a follower receives 100 forks built on top of the same block
   820  func (es *EventHandlerSuite) TestFollowerReceives100Forks() {
   821  	for i := 0; i < 100; i++ {
   822  		// create each proposal as if they are created by some leader
   823  		proposal := createProposal(es.initView+uint64(i)+1, es.initView-1)
   824  		es.voteAggregator.On("AddBlock", proposal).Return(nil).Once()
   825  		// as a follower, I receive these propsals
   826  		err := es.eventhandler.OnReceiveProposal(proposal)
   827  		require.NoError(es.T(), err)
   828  	}
   829  	require.Equal(es.T(), es.endView, es.paceMaker.CurView(), "incorrect view change")
   830  	require.Equal(es.T(), 100, len(es.forks.blocks))
   831  	es.voteAggregator.AssertExpectations(es.T())
   832  }
   833  
   834  func createBlock(view uint64) *model.Block {
   835  	blockID := flow.MakeID(struct {
   836  		BlockID uint64
   837  	}{
   838  		BlockID: view,
   839  	})
   840  	return &model.Block{
   841  		BlockID: blockID,
   842  		View:    uint64(view),
   843  	}
   844  }
   845  
   846  func createBlockWithQC(view uint64, qcview uint64) *model.Block {
   847  	block := createBlock(view)
   848  	parent := createBlock(qcview)
   849  	block.QC = createQC(parent)
   850  	return block
   851  }
   852  
   853  func createQC(parent *model.Block) *flow.QuorumCertificate {
   854  	qc := &flow.QuorumCertificate{
   855  		BlockID:       parent.BlockID,
   856  		View:          parent.View,
   857  		SignerIndices: nil,
   858  		SigData:       nil,
   859  	}
   860  	return qc
   861  }
   862  
   863  func createVote(block *model.Block) *model.Vote {
   864  	return &model.Vote{
   865  		View:     block.View,
   866  		BlockID:  block.BlockID,
   867  		SignerID: flow.ZeroID,
   868  		SigData:  nil,
   869  	}
   870  }
   871  
   872  func createProposal(view uint64, qcview uint64) *model.Proposal {
   873  	block := createBlockWithQC(view, qcview)
   874  	return &model.Proposal{
   875  		Block:   block,
   876  		SigData: nil,
   877  	}
   878  }