github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/consensus/hotstuff/integration/connect_test.go (about)

     1  package integration
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/mock"
     7  	"github.com/stretchr/testify/require"
     8  
     9  	"github.com/onflow/flow-go/consensus/hotstuff/model"
    10  	"github.com/onflow/flow-go/model/flow"
    11  )
    12  
    13  func Connect(t *testing.T, instances []*Instance) {
    14  
    15  	// first, create a map of all instances and a queue for each
    16  	lookup := make(map[flow.Identifier]*Instance)
    17  	for _, in := range instances {
    18  		lookup[in.localID] = in
    19  	}
    20  
    21  	// then, for each instance, initialize a wired up communicator
    22  	for _, sender := range instances {
    23  		sender := sender // avoid capturing loop variable in closure
    24  
    25  		*sender.notifier = *NewMockedCommunicatorConsumer()
    26  		sender.notifier.On("OnOwnProposal", mock.Anything, mock.Anything).Run(
    27  			func(args mock.Arguments) {
    28  				header, ok := args[0].(*flow.Header)
    29  				require.True(t, ok)
    30  
    31  				// sender should always have the parent
    32  				sender.updatingBlocks.RLock()
    33  				_, exists := sender.headers[header.ParentID]
    34  				sender.updatingBlocks.RUnlock()
    35  				if !exists {
    36  					t.Fatalf("parent for proposal not found (sender: %x, parent: %x)", sender.localID, header.ParentID)
    37  				}
    38  
    39  				// convert into proposal immediately
    40  				proposal := model.ProposalFromFlow(header)
    41  
    42  				// store locally and loop back to engine for processing
    43  				sender.ProcessBlock(proposal)
    44  
    45  				// check if we should block the outgoing proposal
    46  				if sender.blockPropOut(proposal) {
    47  					return
    48  				}
    49  
    50  				// iterate through potential receivers
    51  				for _, receiver := range instances {
    52  
    53  					// we should skip ourselves always
    54  					if receiver.localID == sender.localID {
    55  						continue
    56  					}
    57  
    58  					// check if we should block the incoming proposal
    59  					if receiver.blockPropIn(proposal) {
    60  						continue
    61  					}
    62  
    63  					receiver.ProcessBlock(proposal)
    64  				}
    65  			},
    66  		)
    67  		sender.notifier.On("OnOwnVote", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Run(
    68  			func(args mock.Arguments) {
    69  				blockID, ok := args[0].(flow.Identifier)
    70  				require.True(t, ok)
    71  				view, ok := args[1].(uint64)
    72  				require.True(t, ok)
    73  				sigData, ok := args[2].([]byte)
    74  				require.True(t, ok)
    75  				recipientID, ok := args[3].(flow.Identifier)
    76  				require.True(t, ok)
    77  				// convert into vote
    78  				vote := model.VoteFromFlow(sender.localID, blockID, view, sigData)
    79  
    80  				// get the receiver
    81  				receiver, exists := lookup[recipientID]
    82  				if !exists {
    83  					t.Fatalf("recipient doesn't exist (sender: %x, receiver: %x)", sender.localID, recipientID)
    84  				}
    85  
    86  				// if we are next leader we should be receiving our own vote
    87  				if recipientID != sender.localID {
    88  					// check if we should block the outgoing vote
    89  					if sender.blockVoteOut(vote) {
    90  						return
    91  					}
    92  
    93  					// check if e should block the incoming vote
    94  					if receiver.blockVoteIn(vote) {
    95  						return
    96  					}
    97  				}
    98  
    99  				// submit the vote to the receiving event loop (non-blocking)
   100  				receiver.queue <- vote
   101  			},
   102  		)
   103  		sender.notifier.On("OnOwnTimeout", mock.Anything).Run(
   104  			func(args mock.Arguments) {
   105  				timeoutObject, ok := args[0].(*model.TimeoutObject)
   106  				require.True(t, ok)
   107  				// iterate through potential receivers
   108  				for _, receiver := range instances {
   109  
   110  					// we should skip ourselves always
   111  					if receiver.localID == sender.localID {
   112  						continue
   113  					}
   114  
   115  					// check if we should block the outgoing value
   116  					if sender.blockTimeoutObjectOut(timeoutObject) {
   117  						continue
   118  					}
   119  
   120  					// check if we should block the incoming value
   121  					if receiver.blockTimeoutObjectIn(timeoutObject) {
   122  						continue
   123  					}
   124  
   125  					receiver.queue <- timeoutObject
   126  				}
   127  			})
   128  	}
   129  }