github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/p2p/connection/peerManager_integration_test.go (about)

     1  package connection_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/libp2p/go-libp2p/core/host"
     9  	"github.com/libp2p/go-libp2p/core/network"
    10  	"github.com/libp2p/go-libp2p/core/peer"
    11  	"github.com/libp2p/go-libp2p/core/peerstore"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  
    15  	"github.com/onflow/flow-go/model/flow"
    16  	"github.com/onflow/flow-go/module/irrecoverable"
    17  	"github.com/onflow/flow-go/network/p2p"
    18  	"github.com/onflow/flow-go/network/p2p/connection"
    19  	p2ptest "github.com/onflow/flow-go/network/p2p/test"
    20  	"github.com/onflow/flow-go/network/p2p/translator"
    21  	"github.com/onflow/flow-go/network/p2p/utils"
    22  	"github.com/onflow/flow-go/utils/unittest"
    23  )
    24  
    25  // TestPeerManager_Integration tests the correctness of integration between PeerManager and PeerUpdater over
    26  // a fully connected topology.
    27  // PeerManager should be able to connect to all peers using the connector, and must also tear down the connection to
    28  // peers that are excluded from its identity provider.
    29  func TestPeerManager_Integration(t *testing.T) {
    30  	count := 5
    31  	ctx, cancel := context.WithCancel(context.Background())
    32  
    33  	signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx)
    34  
    35  	// create nodes
    36  	idProvider := unittest.NewUpdatableIDProvider(flow.IdentityList{})
    37  	nodes, identities := p2ptest.NodesFixture(t, unittest.IdentifierFixture(), "test_peer_manager", count, idProvider)
    38  	idProvider.SetIdentities(identities)
    39  	p2ptest.StartNodes(t, signalerCtx, nodes)
    40  	defer p2ptest.StopNodes(t, nodes, cancel)
    41  
    42  	thisNode := nodes[0]
    43  	topologyPeers := identities[1:]
    44  
    45  	// adds address of all other nodes into the peer store of this node, so that it can dial them.
    46  	info, invalid := utils.PeerInfosFromIDs(topologyPeers)
    47  	require.Empty(t, invalid)
    48  	for _, i := range info {
    49  		thisNode.Host().Peerstore().SetAddrs(i.ID, i.Addrs, peerstore.PermanentAddrTTL)
    50  	}
    51  
    52  	connector, err := connection.DefaultLibp2pBackoffConnectorFactory()(thisNode.Host())
    53  	require.NoError(t, err)
    54  	// setup
    55  	peerUpdater, err := connection.NewPeerUpdater(&connection.PeerUpdaterConfig{
    56  		PruneConnections: connection.PruningEnabled,
    57  		Logger:           unittest.Logger(),
    58  		Host:             connection.NewConnectorHost(thisNode.Host()),
    59  		Connector:        connector,
    60  	})
    61  	require.NoError(t, err)
    62  
    63  	idTranslator, err := translator.NewFixedTableIdentityTranslator(identities)
    64  	require.NoError(t, err)
    65  
    66  	peerManager := connection.NewPeerManager(unittest.Logger(), connection.DefaultPeerUpdateInterval, peerUpdater)
    67  	peerManager.SetPeersProvider(func() peer.IDSlice {
    68  		// peerManager is furnished with a full topology that connects to all nodes
    69  		// in the topologyPeers.
    70  		peers := peer.IDSlice{}
    71  		for _, id := range topologyPeers {
    72  			peerId, err := idTranslator.GetPeerID(id.NodeID)
    73  			require.NoError(t, err)
    74  			peers = append(peers, peerId)
    75  		}
    76  
    77  		return peers
    78  	})
    79  
    80  	// initially no node should be in peer store of this node.
    81  	require.Empty(t, thisNode.Host().Network().Peers())
    82  	peerManager.ForceUpdatePeers(ctx)
    83  	time.Sleep(1 * time.Second)
    84  	// after a forced update all other nodes must be added to the peer store of this node.
    85  	require.Len(t, thisNode.Host().Network().Peers(), count-1)
    86  	// after a forced update there must be a connection between this node and other nodes
    87  	connectedToAll(t, thisNode.Host(), idTranslator, topologyPeers.NodeIDs())
    88  
    89  	// kicks one node out of the othersIds; this imitates evicting, ejecting, or unstaking a node
    90  	evictedId := topologyPeers[0]     // evicted one
    91  	topologyPeers = topologyPeers[1:] // updates otherIds list
    92  	peerManager.ForceUpdatePeers(ctx)
    93  	time.Sleep(1 * time.Second)
    94  	// after a forced update, the evicted one should be excluded from the peer store.
    95  	require.Len(t, thisNode.Host().Network().Peers(), count-2)
    96  	// there must be a connection between this node and other nodes (except evicted one).
    97  	connectedToAll(t, thisNode.Host(), idTranslator, topologyPeers.NodeIDs())
    98  
    99  	// there must be no connection between this node and evicted one
   100  	peerId, err := idTranslator.GetPeerID(evictedId.NodeID)
   101  	require.NoError(t, err)
   102  	assert.Equal(t, thisNode.Host().Network().Connectedness(peerId), network.NotConnected)
   103  }
   104  
   105  // connectedToAll is a test helper that fails if there is no connection between this host and at least one of the
   106  // nodes in "all".
   107  func connectedToAll(t *testing.T, host host.Host, translator p2p.IDTranslator, all flow.IdentifierList) {
   108  	for _, id := range all {
   109  		peerId, err := translator.GetPeerID(id)
   110  		require.NoError(t, err)
   111  		assert.Equal(t, host.Network().Connectedness(peerId), network.Connected)
   112  	}
   113  }