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

     1  package internal_test
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/libp2p/go-libp2p/core/peer"
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/onflow/flow-go/module/metrics"
    14  	"github.com/onflow/flow-go/network"
    15  	"github.com/onflow/flow-go/network/p2p/node/internal"
    16  	"github.com/onflow/flow-go/utils/unittest"
    17  )
    18  
    19  // TestNewDisallowListCache tests the NewDisallowListCache function. It verifies that the returned disallowListCache
    20  // is not nil.
    21  func TestNewDisallowListCache(t *testing.T) {
    22  	disallowListCache := internal.NewDisallowListCache(uint32(100), unittest.Logger(), metrics.NewNoopCollector())
    23  
    24  	// Verify that the new disallowListCache is not nil
    25  	assert.NotNil(t, disallowListCache)
    26  }
    27  
    28  // TestDisallowFor_SinglePeer tests the DisallowFor function for a single peer. It verifies that the peerID is
    29  // disallow-listed for the given cause and that the cause is returned when the peerID is disallow-listed again.
    30  func TestDisallowFor_SinglePeer(t *testing.T) {
    31  	disallowListCache := internal.NewDisallowListCache(uint32(100), unittest.Logger(), metrics.NewNoopCollector())
    32  	require.NotNil(t, disallowListCache)
    33  
    34  	// disallowing a peerID for a cause when the peerID doesn't exist in the cache
    35  	causes, err := disallowListCache.DisallowFor(peer.ID("peer1"), network.DisallowListedCauseAdmin)
    36  	require.NoError(t, err)
    37  	require.Len(t, causes, 1)
    38  	require.Contains(t, causes, network.DisallowListedCauseAdmin)
    39  
    40  	// disallowing a peerID for a cause when the peerID already exists in the cache
    41  	causes, err = disallowListCache.DisallowFor(peer.ID("peer1"), network.DisallowListedCauseAlsp)
    42  	require.NoError(t, err)
    43  	require.Len(t, causes, 2)
    44  	require.ElementsMatch(t, causes, []network.DisallowListedCause{network.DisallowListedCauseAdmin, network.DisallowListedCauseAlsp})
    45  
    46  	// disallowing a peerID for a duplicate cause
    47  	causes, err = disallowListCache.DisallowFor(peer.ID("peer1"), network.DisallowListedCauseAdmin)
    48  	require.NoError(t, err)
    49  	require.Len(t, causes, 2)
    50  	require.ElementsMatch(t, causes, []network.DisallowListedCause{network.DisallowListedCauseAdmin, network.DisallowListedCauseAlsp})
    51  }
    52  
    53  // TestDisallowFor_MultiplePeers tests the DisallowFor function for multiple peers. It verifies that the peerIDs are
    54  // disallow-listed for the given cause and that the cause is returned when the peerIDs are disallow-listed again.
    55  func TestDisallowFor_MultiplePeers(t *testing.T) {
    56  	disallowListCache := internal.NewDisallowListCache(uint32(100), unittest.Logger(), metrics.NewNoopCollector())
    57  	require.NotNil(t, disallowListCache)
    58  
    59  	for i := 0; i <= 10; i++ {
    60  		// disallowing a peerID for a cause when the peerID doesn't exist in the cache
    61  		causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAdmin)
    62  		require.NoError(t, err)
    63  		require.Len(t, causes, 1)
    64  		require.Contains(t, causes, network.DisallowListedCauseAdmin)
    65  	}
    66  
    67  	for i := 0; i <= 10; i++ {
    68  		// disallowing a peerID for a cause when the peerID already exists in the cache
    69  		causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAlsp)
    70  		require.NoError(t, err)
    71  		require.Len(t, causes, 2)
    72  		require.ElementsMatch(t, causes, []network.DisallowListedCause{network.DisallowListedCauseAdmin, network.DisallowListedCauseAlsp})
    73  	}
    74  
    75  	for i := 0; i <= 10; i++ {
    76  		// getting the disallow-listed causes for a peerID
    77  		causes, disallowListed := disallowListCache.IsDisallowListed(peer.ID(fmt.Sprintf("peer-%d", i)))
    78  		require.True(t, disallowListed)
    79  		require.Len(t, causes, 2)
    80  		require.ElementsMatch(t, causes, []network.DisallowListedCause{network.DisallowListedCauseAdmin, network.DisallowListedCauseAlsp})
    81  	}
    82  }
    83  
    84  // TestAllowFor_SinglePeer is a unit test function to verify the behavior of DisallowListCache for a single peer.
    85  // The test checks the following functionalities in sequence:
    86  // 1. Allowing a peerID for a cause when the peerID already exists in the cache.
    87  // 2. Disallowing the peerID for a cause when the peerID doesn't exist in the cache.
    88  // 3. Getting the disallow-listed causes for the peerID.
    89  // 4. Allowing a peerID for a cause when the peerID already exists in the cache.
    90  // 5. Getting the disallow-listed causes for the peerID.
    91  // 6. Disallowing the peerID for a cause.
    92  // 7. Allowing the peerID for a different cause than it is disallowed when the peerID already exists in the cache.
    93  // 8. Disallowing the peerID for another cause.
    94  // 9. Allowing the peerID for the first cause.
    95  // 10. Allowing the peerID for the second cause.
    96  func TestAllowFor_SinglePeer(t *testing.T) {
    97  	disallowListCache := internal.NewDisallowListCache(uint32(100), unittest.Logger(), metrics.NewNoopCollector())
    98  	require.NotNil(t, disallowListCache)
    99  	peerID := peer.ID("peer1")
   100  
   101  	// allowing the peerID for a cause when the peerID already exists in the cache
   102  	causes := disallowListCache.AllowFor(peerID, network.DisallowListedCauseAdmin)
   103  	require.Len(t, causes, 0)
   104  
   105  	// disallowing the peerID for a cause when the peerID doesn't exist in the cache
   106  	causes, err := disallowListCache.DisallowFor(peerID, network.DisallowListedCauseAdmin)
   107  	require.NoError(t, err)
   108  	require.Len(t, causes, 1)
   109  	require.Contains(t, causes, network.DisallowListedCauseAdmin)
   110  
   111  	// getting the disallow-listed causes for the peerID
   112  	causes, disallowListed := disallowListCache.IsDisallowListed(peerID)
   113  	require.True(t, disallowListed)
   114  	require.Len(t, causes, 1)
   115  	require.Contains(t, causes, network.DisallowListedCauseAdmin)
   116  
   117  	// allowing a peerID for a cause when the peerID already exists in the cache
   118  	causes = disallowListCache.AllowFor(peerID, network.DisallowListedCauseAdmin)
   119  	require.NoError(t, err)
   120  	require.Len(t, causes, 0)
   121  
   122  	// getting the disallow-listed causes for the peerID
   123  	causes, disallowListed = disallowListCache.IsDisallowListed(peerID)
   124  	require.False(t, disallowListed)
   125  	require.Len(t, causes, 0)
   126  
   127  	// disallowing the peerID for a cause
   128  	causes, err = disallowListCache.DisallowFor(peerID, network.DisallowListedCauseAdmin)
   129  	require.NoError(t, err)
   130  	require.Len(t, causes, 1)
   131  
   132  	// allowing the peerID for a different cause than it is disallowed when the peerID already exists in the cache
   133  	causes = disallowListCache.AllowFor(peerID, network.DisallowListedCauseAlsp)
   134  	require.NoError(t, err)
   135  	require.Len(t, causes, 1)
   136  	require.Contains(t, causes, network.DisallowListedCauseAdmin) // the peerID is still disallow-listed for the previous cause
   137  
   138  	// disallowing the peerID for another cause
   139  	causes, err = disallowListCache.DisallowFor(peerID, network.DisallowListedCauseAlsp)
   140  	require.NoError(t, err)
   141  	require.Len(t, causes, 2)
   142  	require.ElementsMatch(t, causes, []network.DisallowListedCause{network.DisallowListedCauseAdmin, network.DisallowListedCauseAlsp})
   143  
   144  	// allowing the peerID for the first cause
   145  	causes = disallowListCache.AllowFor(peerID, network.DisallowListedCauseAdmin)
   146  	require.NoError(t, err)
   147  	require.Len(t, causes, 1)
   148  	require.Contains(t, causes, network.DisallowListedCauseAlsp) // the peerID is still disallow-listed for the previous cause
   149  
   150  	// allowing the peerID for the second cause
   151  	causes = disallowListCache.AllowFor(peerID, network.DisallowListedCauseAlsp)
   152  	require.NoError(t, err)
   153  	require.Len(t, causes, 0)
   154  }
   155  
   156  // TestAllowFor_MultiplePeers_Sequentially is a unit test function to test the behavior of DisallowListCache with multiple peers.
   157  // The test checks the following functionalities in sequence:
   158  // 1. Allowing a peerID for a cause when the peerID doesn't exist in the cache.
   159  // 2. Disallowing peers for a cause.
   160  // 3. Getting the disallow-listed causes for a peerID.
   161  // 4. Allowing the peer ids for a cause different than the one they are disallow-listed for.
   162  // 5. Disallowing the peer ids for a different cause.
   163  // 6. Allowing the peer ids for the first cause.
   164  // 7. Allowing the peer ids for the second cause.
   165  func TestAllowFor_MultiplePeers_Sequentially(t *testing.T) {
   166  	disallowListCache := internal.NewDisallowListCache(uint32(100), unittest.Logger(), metrics.NewNoopCollector())
   167  	require.NotNil(t, disallowListCache)
   168  
   169  	for i := 0; i <= 10; i++ {
   170  		// allowing a peerID for a cause when the peerID doesn't exist in the cache
   171  		causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAdmin)
   172  		require.Len(t, causes, 0)
   173  	}
   174  
   175  	for i := 0; i <= 10; i++ {
   176  		// disallowing peers for a cause
   177  		causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAlsp)
   178  		require.NoError(t, err)
   179  		require.Len(t, causes, 1)
   180  		require.Contains(t, causes, network.DisallowListedCauseAlsp)
   181  	}
   182  
   183  	for i := 0; i <= 10; i++ {
   184  		// getting the disallow-listed causes for a peerID
   185  		causes, disallowListed := disallowListCache.IsDisallowListed(peer.ID(fmt.Sprintf("peer-%d", i)))
   186  		require.True(t, disallowListed)
   187  		require.Len(t, causes, 1)
   188  		require.Contains(t, causes, network.DisallowListedCauseAlsp)
   189  	}
   190  
   191  	for i := 0; i <= 10; i++ {
   192  		// allowing the peer ids for a cause different than the one they are disallow-listed for
   193  		causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAdmin)
   194  		require.Len(t, causes, 1)
   195  		require.Contains(t, causes, network.DisallowListedCauseAlsp)
   196  	}
   197  
   198  	for i := 0; i <= 10; i++ {
   199  		// disallowing the peer ids for a different cause
   200  		causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAdmin)
   201  		require.NoError(t, err)
   202  		require.Len(t, causes, 2)
   203  		require.ElementsMatch(t, causes, []network.DisallowListedCause{network.DisallowListedCauseAdmin, network.DisallowListedCauseAlsp})
   204  	}
   205  
   206  	for i := 0; i <= 10; i++ {
   207  		// allowing the peer ids for the first cause
   208  		causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAdmin)
   209  		require.Len(t, causes, 1)
   210  		require.Contains(t, causes, network.DisallowListedCauseAlsp)
   211  	}
   212  
   213  	for i := 0; i <= 10; i++ {
   214  		// allowing the peer ids for the second cause
   215  		causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAlsp)
   216  		require.Len(t, causes, 0)
   217  	}
   218  }
   219  
   220  // TestAllowFor_MultiplePeers_Concurrently is a unit test function that verifies the behavior of DisallowListCache
   221  // when multiple peerIDs are added and managed concurrently. This test is designed to confirm that DisallowListCache
   222  // works as expected under concurrent access, an important aspect for a system dealing with multiple connections.
   223  //
   224  // The test runs multiple goroutines simultaneously, each handling a different peerID and performs the following
   225  // operations in the sequence:
   226  // 1. Allowing a peerID for a cause when the peerID doesn't exist in the cache.
   227  // 2. Disallowing peers for a cause.
   228  // 3. Getting the disallow-listed causes for a peerID.
   229  // 4. Allowing the peer ids for a cause different than the one they are disallow-listed for.
   230  // 5. Disallowing the peer ids for a different cause.
   231  // 6. Allowing the peer ids for the first cause.
   232  // 7. Allowing the peer ids for the second cause.
   233  // 8. Getting the disallow-listed causes for a peerID.
   234  // 9. Allowing a peerID for a cause when the peerID doesn't exist in the cache for a new set of peers.
   235  func TestAllowFor_MultiplePeers_Concurrently(t *testing.T) {
   236  	disallowListCache := internal.NewDisallowListCache(uint32(100), unittest.Logger(), metrics.NewNoopCollector())
   237  	require.NotNil(t, disallowListCache)
   238  
   239  	var wg sync.WaitGroup
   240  	for i := 0; i <= 10; i++ {
   241  		wg.Add(1)
   242  		go func(i int) {
   243  			defer wg.Done()
   244  
   245  			// allowing a peerID for a cause when the peerID doesn't exist in the cache
   246  			causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAdmin)
   247  			require.Len(t, causes, 0)
   248  		}(i)
   249  	}
   250  	unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish")
   251  
   252  	for i := 0; i <= 10; i++ {
   253  		wg.Add(1)
   254  		go func(i int) {
   255  			defer wg.Done()
   256  
   257  			// disallowing peers for a cause
   258  			causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAlsp)
   259  			require.NoError(t, err)
   260  			require.Len(t, causes, 1)
   261  			require.Contains(t, causes, network.DisallowListedCauseAlsp)
   262  		}(i)
   263  	}
   264  	unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish")
   265  
   266  	for i := 0; i <= 10; i++ {
   267  		wg.Add(1)
   268  		go func(i int) {
   269  			defer wg.Done()
   270  
   271  			// getting the disallow-listed causes for a peerID
   272  			causes, disallowListed := disallowListCache.IsDisallowListed(peer.ID(fmt.Sprintf("peer-%d", i)))
   273  			require.Len(t, causes, 1)
   274  			require.True(t, disallowListed)
   275  			require.Contains(t, causes, network.DisallowListedCauseAlsp)
   276  		}(i)
   277  	}
   278  	unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish")
   279  
   280  	for i := 0; i <= 10; i++ {
   281  		wg.Add(1)
   282  		go func(i int) {
   283  			defer wg.Done()
   284  
   285  			// allowing the peer ids for a cause different than the one they are disallow-listed for
   286  			causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAdmin)
   287  			require.Len(t, causes, 1)
   288  			require.Contains(t, causes, network.DisallowListedCauseAlsp)
   289  		}(i)
   290  	}
   291  	unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish")
   292  
   293  	for i := 0; i <= 10; i++ {
   294  		wg.Add(1)
   295  		go func(i int) {
   296  			defer wg.Done()
   297  
   298  			// disallowing the peer ids for a different cause
   299  			causes, err := disallowListCache.DisallowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAdmin)
   300  			require.NoError(t, err)
   301  			require.Len(t, causes, 2)
   302  			require.ElementsMatch(t, causes, []network.DisallowListedCause{network.DisallowListedCauseAdmin, network.DisallowListedCauseAlsp})
   303  		}(i)
   304  	}
   305  	unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish")
   306  
   307  	for i := 0; i <= 10; i++ {
   308  		wg.Add(1)
   309  		go func(i int) {
   310  			defer wg.Done()
   311  
   312  			// allowing the peer ids for the first cause
   313  			causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAdmin)
   314  			require.Len(t, causes, 1)
   315  			require.Contains(t, causes, network.DisallowListedCauseAlsp)
   316  		}(i)
   317  	}
   318  	unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish")
   319  
   320  	for i := 0; i <= 10; i++ {
   321  		wg.Add(1)
   322  		go func(i int) {
   323  			defer wg.Done()
   324  
   325  			// allowing the peer ids for the second cause
   326  			causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAlsp)
   327  			require.Len(t, causes, 0)
   328  		}(i)
   329  	}
   330  	unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish")
   331  
   332  	for i := 0; i <= 10; i++ {
   333  		wg.Add(1)
   334  		go func(i int) {
   335  			defer wg.Done()
   336  
   337  			// getting the disallow-listed causes for a peerID
   338  			causes, disallowListed := disallowListCache.IsDisallowListed(peer.ID(fmt.Sprintf("peer-%d", i)))
   339  			require.False(t, disallowListed)
   340  			require.Len(t, causes, 0)
   341  		}(i)
   342  	}
   343  	unittest.RequireReturnsBefore(t, wg.Wait, 100*time.Millisecond, "timed out waiting for goroutines to finish")
   344  
   345  	for i := 11; i <= 20; i++ {
   346  		wg.Add(1)
   347  		go func(i int) {
   348  			defer wg.Done()
   349  
   350  			// allowing a peerID for a cause when the peerID doesn't exist in the cache
   351  			causes := disallowListCache.AllowFor(peer.ID(fmt.Sprintf("peer-%d", i)), network.DisallowListedCauseAdmin)
   352  			require.Len(t, causes, 0)
   353  		}(i)
   354  	}
   355  }