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

     1  package scoring_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/libp2p/go-libp2p/core/peer"
     9  	"github.com/stretchr/testify/assert"
    10  	mockery "github.com/stretchr/testify/mock"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/onflow/flow-go/config"
    14  	"github.com/onflow/flow-go/model/flow"
    15  	"github.com/onflow/flow-go/module/irrecoverable"
    16  	"github.com/onflow/flow-go/module/metrics"
    17  	"github.com/onflow/flow-go/module/mock"
    18  	"github.com/onflow/flow-go/network"
    19  	"github.com/onflow/flow-go/network/p2p"
    20  	mockp2p "github.com/onflow/flow-go/network/p2p/mock"
    21  	"github.com/onflow/flow-go/network/p2p/scoring"
    22  	"github.com/onflow/flow-go/utils/slices"
    23  	"github.com/onflow/flow-go/utils/unittest"
    24  )
    25  
    26  // TestSubscriptionProvider_GetSubscribedTopics tests that the SubscriptionProvider returns the correct
    27  // list of topics a peer is subscribed to.
    28  func TestSubscriptionProvider_GetSubscribedTopics(t *testing.T) {
    29  	tp := mockp2p.NewTopicProvider(t)
    30  	cfg, err := config.DefaultConfig()
    31  	require.NoError(t, err)
    32  	idProvider := mock.NewIdentityProvider(t)
    33  
    34  	// set a low update interval to speed up the test
    35  	cfg.NetworkConfig.GossipSub.SubscriptionProvider.UpdateInterval = 100 * time.Millisecond
    36  
    37  	sp, err := scoring.NewSubscriptionProvider(&scoring.SubscriptionProviderConfig{
    38  		Logger: unittest.Logger(),
    39  		TopicProviderOracle: func() p2p.TopicProvider {
    40  			return tp
    41  		},
    42  		Params:                  &cfg.NetworkConfig.GossipSub.SubscriptionProvider,
    43  		HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(),
    44  		IdProvider:              idProvider,
    45  		NetworkingType:          network.PrivateNetwork,
    46  	})
    47  	require.NoError(t, err)
    48  
    49  	tp.On("GetTopics").Return([]string{"topic1", "topic2", "topic3"}).Maybe()
    50  
    51  	peer1 := unittest.PeerIdFixture(t)
    52  	peer2 := unittest.PeerIdFixture(t)
    53  	peer3 := unittest.PeerIdFixture(t)
    54  
    55  	idProvider.On("ByPeerID", mockery.Anything).Return(unittest.IdentityFixture(), true).Maybe()
    56  
    57  	// mock peers 1 and 2 subscribed to topic 1 (along with other random peers)
    58  	tp.On("ListPeers", "topic1").Return(append([]peer.ID{peer1, peer2}, unittest.PeerIdFixtures(t, 10)...))
    59  	// mock peers 2 and 3 subscribed to topic 2 (along with other random peers)
    60  	tp.On("ListPeers", "topic2").Return(append([]peer.ID{peer2, peer3}, unittest.PeerIdFixtures(t, 10)...))
    61  	// mock peers 1 and 3 subscribed to topic 3 (along with other random peers)
    62  	tp.On("ListPeers", "topic3").Return(append([]peer.ID{peer1, peer3}, unittest.PeerIdFixtures(t, 10)...))
    63  
    64  	ctx, cancel := context.WithCancel(context.Background())
    65  	defer func() {
    66  		cancel()
    67  		unittest.RequireCloseBefore(t, sp.Done(), 1*time.Second, "subscription provider did not stop in time")
    68  	}()
    69  	signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx)
    70  	sp.Start(signalerCtx)
    71  	unittest.RequireCloseBefore(t, sp.Ready(), 1*time.Second, "subscription provider did not start in time")
    72  
    73  	// As the calls to the TopicProvider are asynchronous, we need to wait for the goroutines to finish.
    74  	assert.Eventually(t, func() bool {
    75  		return slices.AreStringSlicesEqual([]string{"topic1", "topic3"}, sp.GetSubscribedTopics(peer1))
    76  	}, 1*time.Second, 100*time.Millisecond)
    77  
    78  	assert.Eventually(t, func() bool {
    79  		return slices.AreStringSlicesEqual([]string{"topic1", "topic2"}, sp.GetSubscribedTopics(peer2))
    80  	}, 1*time.Second, 100*time.Millisecond)
    81  
    82  	assert.Eventually(t, func() bool {
    83  		return slices.AreStringSlicesEqual([]string{"topic2", "topic3"}, sp.GetSubscribedTopics(peer3))
    84  	}, 1*time.Second, 100*time.Millisecond)
    85  }
    86  
    87  // TestSubscriptionProvider_GetSubscribedTopics_SkippingUnknownPeers tests that the SubscriptionProvider skips
    88  // unknown peers when returning the list of topics a peer is subscribed to. In other words, if a peer is unknown,
    89  // the SubscriptionProvider should not keep track of its subscriptions.
    90  func TestSubscriptionProvider_GetSubscribedTopics_SkippingUnknownPeers(t *testing.T) {
    91  	tp := mockp2p.NewTopicProvider(t)
    92  	cfg, err := config.DefaultConfig()
    93  	require.NoError(t, err)
    94  	idProvider := mock.NewIdentityProvider(t)
    95  
    96  	// set a low update interval to speed up the test
    97  	cfg.NetworkConfig.GossipSub.SubscriptionProvider.UpdateInterval = 100 * time.Millisecond
    98  
    99  	sp, err := scoring.NewSubscriptionProvider(&scoring.SubscriptionProviderConfig{
   100  		Logger: unittest.Logger(),
   101  		TopicProviderOracle: func() p2p.TopicProvider {
   102  			return tp
   103  		},
   104  		Params:                  &cfg.NetworkConfig.GossipSub.SubscriptionProvider,
   105  		HeroCacheMetricsFactory: metrics.NewNoopHeroCacheMetricsFactory(),
   106  		IdProvider:              idProvider,
   107  		NetworkingType:          network.PrivateNetwork,
   108  	})
   109  	require.NoError(t, err)
   110  
   111  	tp.On("GetTopics").Return([]string{"topic1", "topic2", "topic3"}).Maybe()
   112  
   113  	peer1 := unittest.PeerIdFixture(t)
   114  	peer2 := unittest.PeerIdFixture(t)
   115  	peer3 := unittest.PeerIdFixture(t)
   116  
   117  	// mock peers 1 and 2 as a known peer; peer 3 as an unknown peer
   118  	idProvider.On("ByPeerID", mockery.Anything).
   119  		Return(func(pid peer.ID) *flow.Identity {
   120  			if pid == peer1 || pid == peer2 {
   121  				return unittest.IdentityFixture()
   122  			}
   123  			return nil
   124  		}, func(pid peer.ID) bool {
   125  			if pid == peer1 || pid == peer2 {
   126  				return true
   127  			}
   128  			return false
   129  		}).Maybe()
   130  
   131  	// mock peers 1 and 2 subscribed to topic 1 (along with other random peers)
   132  	tp.On("ListPeers", "topic1").Return(append([]peer.ID{peer1, peer2}, unittest.PeerIdFixtures(t, 10)...))
   133  	// mock peers 2 and 3 subscribed to topic 2 (along with other random peers)
   134  	tp.On("ListPeers", "topic2").Return(append([]peer.ID{peer2, peer3}, unittest.PeerIdFixtures(t, 10)...))
   135  	// mock peers 1 and 3 subscribed to topic 3 (along with other random peers)
   136  	tp.On("ListPeers", "topic3").Return(append([]peer.ID{peer1, peer3}, unittest.PeerIdFixtures(t, 10)...))
   137  
   138  	ctx, cancel := context.WithCancel(context.Background())
   139  	defer func() {
   140  		cancel()
   141  		unittest.RequireCloseBefore(t, sp.Done(), 1*time.Second, "subscription provider did not stop in time")
   142  	}()
   143  	signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx)
   144  	sp.Start(signalerCtx)
   145  	unittest.RequireCloseBefore(t, sp.Ready(), 1*time.Second, "subscription provider did not start in time")
   146  
   147  	// As the calls to the TopicProvider are asynchronous, we need to wait for the goroutines to finish.
   148  	// peer 1 should be eventually subscribed to topic 1 and topic 3; while peer 3 should not have any subscriptions record since it is unknown
   149  	assert.Eventually(t, func() bool {
   150  		return slices.AreStringSlicesEqual([]string{"topic1", "topic3"}, sp.GetSubscribedTopics(peer1)) &&
   151  			slices.AreStringSlicesEqual([]string{}, sp.GetSubscribedTopics(peer3))
   152  	}, 1*time.Second, 100*time.Millisecond)
   153  
   154  	// peer 2 should be eventually subscribed to topic 1 and topic 2; while peer 3 should not have any subscriptions record since it is unknown
   155  	assert.Eventually(t, func() bool {
   156  		return slices.AreStringSlicesEqual([]string{"topic1", "topic2"}, sp.GetSubscribedTopics(peer2)) &&
   157  			slices.AreStringSlicesEqual([]string{}, sp.GetSubscribedTopics(peer3))
   158  	}, 1*time.Second, 100*time.Millisecond)
   159  }