github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/test/cohort1/network_test.go (about) 1 package cohort1 2 3 import ( 4 "context" 5 "fmt" 6 "math/rand" 7 "regexp" 8 "strings" 9 "sync" 10 "testing" 11 "time" 12 13 "github.com/libp2p/go-libp2p/core/peer" 14 "github.com/libp2p/go-libp2p/p2p/net/swarm" 15 "github.com/rs/zerolog" 16 "github.com/stretchr/testify/assert" 17 mockery "github.com/stretchr/testify/mock" 18 "github.com/stretchr/testify/require" 19 "github.com/stretchr/testify/suite" 20 "go.uber.org/atomic" 21 "golang.org/x/time/rate" 22 23 "github.com/onflow/flow-go/config" 24 "github.com/onflow/flow-go/model/flow" 25 "github.com/onflow/flow-go/model/flow/filter" 26 libp2pmessage "github.com/onflow/flow-go/model/libp2p/message" 27 "github.com/onflow/flow-go/model/messages" 28 "github.com/onflow/flow-go/module/irrecoverable" 29 "github.com/onflow/flow-go/module/metrics" 30 "github.com/onflow/flow-go/module/observable" 31 "github.com/onflow/flow-go/network" 32 "github.com/onflow/flow-go/network/channels" 33 "github.com/onflow/flow-go/network/internal/p2pfixtures" 34 "github.com/onflow/flow-go/network/internal/testutils" 35 "github.com/onflow/flow-go/network/message" 36 "github.com/onflow/flow-go/network/mocknetwork" 37 "github.com/onflow/flow-go/network/p2p" 38 p2pnode "github.com/onflow/flow-go/network/p2p/node" 39 p2ptest "github.com/onflow/flow-go/network/p2p/test" 40 "github.com/onflow/flow-go/network/p2p/unicast/ratelimit" 41 "github.com/onflow/flow-go/network/p2p/utils/ratelimiter" 42 "github.com/onflow/flow-go/network/underlay" 43 "github.com/onflow/flow-go/utils/unittest" 44 ) 45 46 // libp2p emits a call to `Protect` with a topic-specific tag upon establishing each peering connection in a GossipSub mesh, see: 47 // https://github.com/libp2p/go-libp2p-pubsub/blob/master/tag_tracer.go 48 // One way to make sure such a mesh has formed, asynchronously, in unit tests, is to wait for libp2p.GossipSub such calls, 49 // and that's what we do with tagsObserver. 50 // Usage: 51 // The tagsObserver struct observes the OnNext, OnError, and OnComplete events related to peer tags. 52 // A channel 'tags' is used to communicate these tags, and the observer is subscribed to the observable connection manager. 53 // Advantages: 54 // Using tag observables helps understand the connectivity between different peers, 55 // and can be valuable in testing scenarios where network connectivity is critical. 56 // Issues: 57 // - Deviation from Production Code: This tag observation might be unique to the test environment, 58 // and therefore not reflect the behavior of the production code. 59 // - Mask Issues in the Production Environment: The observables are tied to testing and might 60 // lead to behaviors or errors that are masked or not evident within the actual production environment. 61 // 62 // TODO: Evaluate the necessity of tag observables in this test. Consider addressing the deviation from 63 // production code and potential mask issues in the production environment. Evaluate the possibility 64 // of removing this part eventually. 65 type tagsObserver struct { 66 tags chan string 67 log zerolog.Logger 68 } 69 70 func (co *tagsObserver) OnNext(peertag interface{}) { 71 pt, ok := peertag.(testutils.PeerTag) 72 73 if ok { 74 co.tags <- fmt.Sprintf("peer: %v tag: %v", pt.Peer, pt.Tag) 75 } 76 77 } 78 func (co *tagsObserver) OnError(err error) { 79 co.log.Error().Err(err).Msg("Tags Observer closed on an error") 80 close(co.tags) 81 } 82 func (co *tagsObserver) OnComplete() { 83 close(co.tags) 84 } 85 86 // TODO: eventually this should be moved to the p2pnet package. 87 type NetworkTestSuite struct { 88 suite.Suite 89 sync.RWMutex 90 size int // used to determine number of networks under test 91 libP2PNodes []p2p.LibP2PNode 92 networks []*underlay.Network 93 obs chan string // used to keep track of Protect events tagged by pubsub messages 94 ids []*flow.Identity 95 metrics *metrics.NoopCollector // no-op performance monitoring simulation 96 logger zerolog.Logger 97 providers []*unittest.UpdatableIDProvider 98 sporkId flow.Identifier 99 mwCancel context.CancelFunc 100 mwCtx irrecoverable.SignalerContext 101 } 102 103 // TestNetworkTestSuit runs all the test methods in this test suit 104 func TestNetworkTestSuite(t *testing.T) { 105 // should not run in parallel, some tests are stateful. 106 suite.Run(t, new(NetworkTestSuite)) 107 } 108 109 // SetupTest initiates the test setups prior to each test 110 func (suite *NetworkTestSuite) SetupTest() { 111 suite.logger = unittest.Logger() 112 113 suite.size = 2 // operates on two networks 114 suite.metrics = metrics.NewNoopCollector() 115 116 // create and start the networks and inject a connection observer 117 peerChannel := make(chan string) 118 ob := tagsObserver{ 119 tags: peerChannel, 120 log: suite.logger, 121 } 122 123 suite.sporkId = unittest.IdentifierFixture() 124 125 libP2PNodes := make([]p2p.LibP2PNode, 0) 126 identities := make(flow.IdentityList, 0) 127 tagObservables := make([]observable.Observable, 0) 128 idProvider := unittest.NewUpdatableIDProvider(flow.IdentityList{}) 129 defaultFlowConfig, err := config.DefaultConfig() 130 require.NoError(suite.T(), err) 131 defaultFlowConfig.NetworkConfig.Unicast.UnicastManager.CreateStreamBackoffDelay = 1 * time.Millisecond 132 133 opts := []p2ptest.NodeFixtureParameterOption{p2ptest.WithUnicastHandlerFunc(nil)} 134 135 for i := 0; i < suite.size; i++ { 136 connManager, err := testutils.NewTagWatchingConnManager( 137 unittest.Logger(), 138 metrics.NewNoopCollector(), 139 &defaultFlowConfig.NetworkConfig.ConnectionManager) 140 require.NoError(suite.T(), err) 141 142 opts = append(opts, 143 p2ptest.WithConnectionManager(connManager), 144 p2ptest.WithRole(flow.RoleExecution), 145 p2ptest.OverrideFlowConfig(defaultFlowConfig)) // to suppress exponential backoff 146 node, nodeId := p2ptest.NodeFixture(suite.T(), 147 suite.sporkId, 148 suite.T().Name(), 149 idProvider, 150 opts...) 151 libP2PNodes = append(libP2PNodes, node) 152 identities = append(identities, &nodeId) 153 tagObservables = append(tagObservables, connManager) 154 } 155 idProvider.SetIdentities(identities) 156 157 suite.ids = identities 158 suite.libP2PNodes = libP2PNodes 159 160 suite.networks, suite.providers = testutils.NetworksFixture(suite.T(), suite.sporkId, suite.ids, suite.libP2PNodes) 161 for _, observableConnMgr := range tagObservables { 162 observableConnMgr.Subscribe(&ob) 163 } 164 suite.obs = peerChannel 165 166 require.Len(suite.Suite.T(), tagObservables, suite.size) 167 require.Len(suite.Suite.T(), suite.ids, suite.size) 168 169 ctx, cancel := context.WithCancel(context.Background()) 170 suite.mwCancel = cancel 171 172 suite.mwCtx = irrecoverable.NewMockSignalerContext(suite.T(), ctx) 173 174 testutils.StartNodes(suite.mwCtx, suite.T(), suite.libP2PNodes) 175 176 for i, net := range suite.networks { 177 unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, libP2PNodes[i]) 178 net.Start(suite.mwCtx) 179 unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, net) 180 } 181 } 182 183 func (suite *NetworkTestSuite) TearDownTest() { 184 suite.mwCancel() 185 186 testutils.StopComponents(suite.T(), suite.networks, 1*time.Second) 187 testutils.StopComponents(suite.T(), suite.libP2PNodes, 1*time.Second) 188 suite.libP2PNodes = nil 189 suite.ids = nil 190 suite.size = 0 191 } 192 193 // TestUpdateNodeAddresses tests that the UpdateNodeAddresses method correctly updates 194 // the addresses of the staked network participants. 195 func (suite *NetworkTestSuite) TestUpdateNodeAddresses() { 196 ctx, cancel := context.WithCancel(suite.mwCtx) 197 irrecoverableCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) 198 199 // create a new staked identity 200 ids, libP2PNodes := testutils.LibP2PNodeForNetworkFixture(suite.T(), suite.sporkId, 1) 201 idProvider := unittest.NewUpdatableIDProvider(ids) 202 networkCfg := testutils.NetworkConfigFixture( 203 suite.T(), 204 *ids[0], 205 idProvider, 206 suite.sporkId, 207 libP2PNodes[0]) 208 newNet, err := underlay.NewNetwork(networkCfg) 209 require.NoError(suite.T(), err) 210 require.Len(suite.T(), ids, 1) 211 newId := ids[0] 212 213 // start up nodes and peer managers 214 testutils.StartNodes(irrecoverableCtx, suite.T(), libP2PNodes) 215 defer testutils.StopComponents(suite.T(), libP2PNodes, 1*time.Second) 216 217 newNet.Start(irrecoverableCtx) 218 defer testutils.StopComponents(suite.T(), []network.EngineRegistry{newNet}, 1*time.Second) 219 unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, newNet) 220 221 idList := flow.IdentityList(append(suite.ids, newId)) 222 223 // needed to enable ID translation 224 suite.providers[0].SetIdentities(idList) 225 226 // unicast should fail to send because no address is known yet for the new identity 227 con, err := suite.networks[0].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) 228 require.NoError(suite.T(), err) 229 err = con.Unicast(&libp2pmessage.TestMessage{ 230 Text: "TestUpdateNodeAddresses", 231 }, newId.NodeID) 232 require.True(suite.T(), strings.Contains(err.Error(), swarm.ErrNoAddresses.Error())) 233 234 // update the addresses 235 suite.networks[0].UpdateNodeAddresses() 236 237 // now the message should send successfully 238 err = con.Unicast(&libp2pmessage.TestMessage{ 239 Text: "TestUpdateNodeAddresses", 240 }, newId.NodeID) 241 require.NoError(suite.T(), err) 242 243 cancel() 244 unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, newNet) 245 } 246 247 func (suite *NetworkTestSuite) TestUnicastRateLimit_Messages() { 248 unittest.SkipUnless(suite.T(), unittest.TEST_FLAKY, "flaky") 249 // limiter limit will be set to 5 events/sec the 6th event per interval will be rate limited 250 limit := rate.Limit(5) 251 252 // burst per interval 253 burst := 5 254 255 for _, net := range suite.networks { 256 require.NoError(suite.T(), net.Subscribe(channels.TestNetworkChannel)) 257 } 258 259 messageRateLimiter := ratelimiter.NewRateLimiter(limit, burst, 3*time.Second) 260 261 // we only expect messages from the first networks on the test suite 262 expectedPID, err := unittest.PeerIDFromFlowID(suite.ids[0]) 263 require.NoError(suite.T(), err) 264 265 // the onRateLimit call back we will use to keep track of how many times a rate limit happens. 266 rateLimits := atomic.NewUint64(0) 267 268 onRateLimit := func(peerID peer.ID, role, msgType, topic, reason string) { 269 require.Equal(suite.T(), reason, ratelimit.ReasonMessageCount.String()) 270 require.Equal(suite.T(), expectedPID, peerID) 271 // update hook calls 272 rateLimits.Inc() 273 } 274 275 // setup rate limit distributor that will be used to track the number of rate limits via the onRateLimit callback. 276 consumer := testutils.NewRateLimiterConsumer(onRateLimit) 277 distributor := ratelimit.NewUnicastRateLimiterDistributor() 278 distributor.AddConsumer(consumer) 279 280 opts := []ratelimit.RateLimitersOption{ratelimit.WithMessageRateLimiter(messageRateLimiter), ratelimit.WithNotifier(distributor), ratelimit.WithDisabledRateLimiting(false)} 281 rateLimiters := ratelimit.NewRateLimiters(opts...) 282 283 defaultFlowConfig, err := config.DefaultConfig() 284 require.NoError(suite.T(), err) 285 defaultFlowConfig.NetworkConfig.Unicast.UnicastManager.CreateStreamBackoffDelay = 1 * time.Millisecond 286 287 idProvider := unittest.NewUpdatableIDProvider(suite.ids) 288 ids, libP2PNodes := testutils.LibP2PNodeForNetworkFixture(suite.T(), 289 suite.sporkId, 290 1, 291 p2ptest.WithUnicastRateLimitDistributor(distributor), 292 p2ptest.OverrideFlowConfig(defaultFlowConfig), // to suppress exponential backoff 293 p2ptest.WithConnectionGater(p2ptest.NewConnectionGater(idProvider, func(pid peer.ID) error { 294 if messageRateLimiter.IsRateLimited(pid) { 295 return fmt.Errorf("rate-limited peer") 296 } 297 return nil 298 }))) 299 idProvider.SetIdentities(append(suite.ids, ids...)) 300 301 netCfg := testutils.NetworkConfigFixture( 302 suite.T(), 303 *ids[0], 304 idProvider, 305 suite.sporkId, 306 libP2PNodes[0]) 307 newNet, err := underlay.NewNetwork( 308 netCfg, 309 underlay.WithUnicastRateLimiters(rateLimiters), 310 underlay.WithPeerManagerFilters(testutils.IsRateLimitedPeerFilter(messageRateLimiter))) 311 require.NoError(suite.T(), err) 312 313 require.Len(suite.T(), ids, 1) 314 newId := ids[0] 315 idList := flow.IdentityList(append(suite.ids, newId)) 316 317 suite.providers[0].SetIdentities(idList) 318 319 ctx, cancel := context.WithCancel(suite.mwCtx) 320 irrecoverableCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) 321 testutils.StartNodes(irrecoverableCtx, suite.T(), libP2PNodes) 322 defer testutils.StopComponents(suite.T(), libP2PNodes, 1*time.Second) 323 testutils.StartNetworks(irrecoverableCtx, suite.T(), []network.EngineRegistry{newNet}) 324 325 calls := atomic.NewUint64(0) 326 ch := make(chan struct{}) 327 // registers an engine on the new network 328 newEngine := &mocknetwork.MessageProcessor{} 329 _, err = newNet.Register(channels.TestNetworkChannel, newEngine) 330 require.NoError(suite.T(), err) 331 newEngine.On("Process", channels.TestNetworkChannel, suite.ids[0].NodeID, mockery.Anything).Run(func(args mockery.Arguments) { 332 calls.Inc() 333 if calls.Load() >= 5 { 334 close(ch) 335 } 336 }).Return(nil) 337 338 // needed to enable ID translation 339 suite.providers[0].SetIdentities(idList) 340 341 // update the addresses 342 suite.networks[0].UpdateNodeAddresses() 343 344 // add our sender node as a direct peer to our receiving node, this allows us to ensure 345 // that connections to peers that are rate limited are completely prune. IsConnected will 346 // return true only if the node is a direct peer of the other, after rate limiting this direct 347 // peer should be removed by the peer manager. 348 p2ptest.LetNodesDiscoverEachOther(suite.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], suite.libP2PNodes[0]}, flow.IdentityList{ids[0], suite.ids[0]}) 349 p2ptest.TryConnectionAndEnsureConnected(suite.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], suite.libP2PNodes[0]}) 350 351 con0, err := suite.networks[0].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) 352 require.NoError(suite.T(), err) 353 354 // with the rate limit configured to 5 msg/sec we send 10 messages at once and expect the rate limiter 355 // to be invoked at-least once. We send 10 messages due to the flakiness that is caused by async stream 356 // handling of streams. 357 for i := 0; i < 10; i++ { 358 err = con0.Unicast(&libp2pmessage.TestMessage{ 359 Text: fmt.Sprintf("hello-%d", i), 360 }, newId.NodeID) 361 require.NoError(suite.T(), err) 362 } 363 // wait for all rate limits before shutting down network 364 unittest.RequireCloseBefore(suite.T(), ch, 100*time.Millisecond, "could not stop rate limit test ch on time") 365 366 // sleep for 1 seconds to allow connection pruner to prune connections 367 time.Sleep(2 * time.Second) 368 369 // ensure connection to rate limited peer is pruned 370 p2ptest.EnsureNotConnectedBetweenGroups(suite.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{suite.libP2PNodes[0]}) 371 p2pfixtures.EnsureNoStreamCreationBetweenGroups(suite.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{suite.libP2PNodes[0]}) 372 373 // eventually the rate limited node should be able to reconnect and send messages 374 require.Eventually(suite.T(), func() bool { 375 err = con0.Unicast(&libp2pmessage.TestMessage{ 376 Text: "hello", 377 }, newId.NodeID) 378 return err == nil 379 }, 3*time.Second, 100*time.Millisecond) 380 381 // shutdown our network so that each message can be processed 382 cancel() 383 unittest.RequireCloseBefore(suite.T(), libP2PNodes[0].Done(), 100*time.Millisecond, "could not stop libp2p node on time") 384 unittest.RequireCloseBefore(suite.T(), newNet.Done(), 100*time.Millisecond, "could not stop network on time") 385 386 // expect our rate limited peer callback to be invoked once 387 require.True(suite.T(), rateLimits.Load() > 0) 388 } 389 390 func (suite *NetworkTestSuite) TestUnicastRateLimit_Bandwidth() { 391 // limiter limit will be set up to 1000 bytes/sec 392 limit := rate.Limit(1000) 393 394 // burst per interval 395 burst := 1000 396 397 // we only expect messages from the first network on the test suite 398 expectedPID, err := unittest.PeerIDFromFlowID(suite.ids[0]) 399 require.NoError(suite.T(), err) 400 401 // setup bandwidth rate limiter 402 bandwidthRateLimiter := ratelimit.NewBandWidthRateLimiter(limit, burst, 4*time.Second) 403 404 // the onRateLimit call back we will use to keep track of how many times a rate limit happens 405 // after 5 rate limits we will close ch. 406 ch := make(chan struct{}) 407 rateLimits := atomic.NewUint64(0) 408 onRateLimit := func(peerID peer.ID, role, msgType, topic, reason string) { 409 require.Equal(suite.T(), reason, ratelimit.ReasonBandwidth.String()) 410 411 // we only expect messages from the first network on the test suite 412 require.NoError(suite.T(), err) 413 require.Equal(suite.T(), expectedPID, peerID) 414 // update hook calls 415 rateLimits.Inc() 416 close(ch) 417 } 418 419 consumer := testutils.NewRateLimiterConsumer(onRateLimit) 420 distributor := ratelimit.NewUnicastRateLimiterDistributor() 421 distributor.AddConsumer(consumer) 422 opts := []ratelimit.RateLimitersOption{ratelimit.WithBandwidthRateLimiter(bandwidthRateLimiter), ratelimit.WithNotifier(distributor), ratelimit.WithDisabledRateLimiting(false)} 423 rateLimiters := ratelimit.NewRateLimiters(opts...) 424 425 defaultFlowConfig, err := config.DefaultConfig() 426 require.NoError(suite.T(), err) 427 defaultFlowConfig.NetworkConfig.Unicast.UnicastManager.CreateStreamBackoffDelay = 1 * time.Millisecond 428 429 idProvider := unittest.NewUpdatableIDProvider(suite.ids) 430 // create a new staked identity 431 ids, libP2PNodes := testutils.LibP2PNodeForNetworkFixture(suite.T(), 432 suite.sporkId, 433 1, 434 p2ptest.WithUnicastRateLimitDistributor(distributor), 435 p2ptest.OverrideFlowConfig(defaultFlowConfig), // to suppress exponential backoff 436 p2ptest.WithConnectionGater(p2ptest.NewConnectionGater(idProvider, func(pid peer.ID) error { 437 // create connection gater, connection gater will refuse connections from rate limited nodes 438 if bandwidthRateLimiter.IsRateLimited(pid) { 439 return fmt.Errorf("rate-limited peer") 440 } 441 442 return nil 443 }))) 444 idProvider.SetIdentities(append(suite.ids, ids...)) 445 suite.providers[0].SetIdentities(append(suite.ids, ids...)) 446 447 netCfg := testutils.NetworkConfigFixture( 448 suite.T(), 449 *ids[0], 450 idProvider, 451 suite.sporkId, 452 libP2PNodes[0]) 453 newNet, err := underlay.NewNetwork( 454 netCfg, 455 underlay.WithUnicastRateLimiters(rateLimiters), 456 underlay.WithPeerManagerFilters(testutils.IsRateLimitedPeerFilter(bandwidthRateLimiter))) 457 require.NoError(suite.T(), err) 458 require.Len(suite.T(), ids, 1) 459 newId := ids[0] 460 461 ctx, cancel := context.WithCancel(suite.mwCtx) 462 irrecoverableCtx := irrecoverable.NewMockSignalerContext(suite.T(), ctx) 463 464 testutils.StartNodes(irrecoverableCtx, suite.T(), libP2PNodes) 465 defer testutils.StopComponents(suite.T(), libP2PNodes, 1*time.Second) 466 467 testutils.StartNetworks(irrecoverableCtx, suite.T(), []network.EngineRegistry{newNet}) 468 unittest.RequireComponentsReadyBefore(suite.T(), 1*time.Second, newNet) 469 470 // registers an engine on the new network so that it can receive messages on the TestNetworkChannel 471 newEngine := &mocknetwork.MessageProcessor{} 472 _, err = newNet.Register(channels.TestNetworkChannel, newEngine) 473 require.NoError(suite.T(), err) 474 newEngine.On("Process", channels.TestNetworkChannel, suite.ids[0].NodeID, mockery.Anything).Return(nil) 475 476 idList := flow.IdentityList(append(suite.ids, newId)) 477 478 // needed to enable ID translation 479 suite.providers[0].SetIdentities(idList) 480 481 // update the addresses 482 suite.networks[0].UpdateNodeAddresses() 483 484 // add our sender node as a direct peer to our receiving node, this allows us to ensure 485 // that connections to peers that are rate limited are completely prune. IsConnected will 486 // return true only if the node is a direct peer of the other, after rate limiting this direct 487 // peer should be removed by the peer manager. 488 p2ptest.LetNodesDiscoverEachOther(suite.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0], suite.libP2PNodes[0]}, flow.IdentityList{ids[0], suite.ids[0]}) 489 490 // create message with about 400bytes (300 random bytes + 100bytes message info) 491 b := make([]byte, 300) 492 for i := range b { 493 b[i] = byte('X') 494 } 495 // send 3 messages at once with a size of 400 bytes each. The third message will be rate limited 496 // as it is more than our allowed bandwidth of 1000 bytes. 497 con0, err := suite.networks[0].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) 498 require.NoError(suite.T(), err) 499 for i := 0; i < 3; i++ { 500 err = con0.Unicast(&libp2pmessage.TestMessage{ 501 Text: string(b), 502 }, newId.NodeID) 503 require.NoError(suite.T(), err) 504 } 505 506 // wait for all rate limits before shutting down network 507 unittest.RequireCloseBefore(suite.T(), ch, 100*time.Millisecond, "could not stop on rate limit test ch on time") 508 509 // sleep for 1 seconds to allow connection pruner to prune connections 510 time.Sleep(1 * time.Second) 511 512 // ensure connection to rate limited peer is pruned 513 p2ptest.EnsureNotConnectedBetweenGroups(suite.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{suite.libP2PNodes[0]}) 514 p2pfixtures.EnsureNoStreamCreationBetweenGroups(suite.T(), ctx, []p2p.LibP2PNode{libP2PNodes[0]}, []p2p.LibP2PNode{suite.libP2PNodes[0]}) 515 516 // eventually the rate limited node should be able to reconnect and send messages 517 require.Eventually(suite.T(), func() bool { 518 err = con0.Unicast(&libp2pmessage.TestMessage{ 519 Text: "", 520 }, newId.NodeID) 521 return err == nil 522 }, 5*time.Second, 100*time.Millisecond) 523 524 // shutdown our network so that each message can be processed 525 cancel() 526 unittest.RequireComponentsDoneBefore(suite.T(), 5*time.Second, newNet) 527 528 // expect our rate limited peer callback to be invoked once 529 require.Equal(suite.T(), uint64(1), rateLimits.Load()) 530 } 531 532 func (suite *NetworkTestSuite) createOverlay(provider *unittest.UpdatableIDProvider) *mocknetwork.Overlay { 533 overlay := &mocknetwork.Overlay{} 534 overlay.On("Identities").Maybe().Return(func() flow.IdentityList { 535 return provider.Identities(filter.Any) 536 }) 537 overlay.On("Topology").Maybe().Return(func() flow.IdentityList { 538 return provider.Identities(filter.Any) 539 }, nil) 540 // this test is not testing the topic validator, especially in spoofing, 541 // so we always return a valid identity. We only care about the node role for the test TestMaxMessageSize_Unicast 542 // where EN are the only node authorized to send chunk data response. 543 identityOpts := unittest.WithRole(flow.RoleExecution) 544 overlay.On("Identity", mockery.AnythingOfType("peer.ID")).Maybe().Return(unittest.IdentityFixture(identityOpts), true) 545 return overlay 546 } 547 548 // TestPing sends a message from the first network of the test suit to the last one and checks that the 549 // last network receives the message and that the message is correctly decoded. 550 func (suite *NetworkTestSuite) TestPing() { 551 receiveWG := sync.WaitGroup{} 552 receiveWG.Add(1) 553 // extracts sender id based on the mock option 554 var err error 555 556 senderNodeIndex := 0 557 targetNodeIndex := suite.size - 1 558 559 expectedPayload := "TestPingContentReception" 560 // mocks a target engine on the last node of the test suit that will receive the message on the test channel. 561 targetEngine := &mocknetwork.MessageProcessor{} // target engine on the last node of the test suit that will receive the message 562 _, err = suite.networks[targetNodeIndex].Register(channels.TestNetworkChannel, targetEngine) 563 require.NoError(suite.T(), err) 564 // target engine must receive the message once with the expected payload 565 targetEngine.On("Process", mockery.Anything, mockery.Anything, mockery.Anything). 566 Run(func(args mockery.Arguments) { 567 receiveWG.Done() 568 569 msgChannel, ok := args[0].(channels.Channel) 570 require.True(suite.T(), ok) 571 require.Equal(suite.T(), channels.TestNetworkChannel, msgChannel) // channel 572 573 msgOriginID, ok := args[1].(flow.Identifier) 574 require.True(suite.T(), ok) 575 require.Equal(suite.T(), suite.ids[senderNodeIndex].NodeID, msgOriginID) // sender id 576 577 msgPayload, ok := args[2].(*libp2pmessage.TestMessage) 578 require.True(suite.T(), ok) 579 require.Equal(suite.T(), expectedPayload, msgPayload.Text) // payload 580 }).Return(nil).Once() 581 582 // sends a direct message from first node to the last node 583 con0, err := suite.networks[senderNodeIndex].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) 584 require.NoError(suite.T(), err) 585 err = con0.Unicast(&libp2pmessage.TestMessage{ 586 Text: expectedPayload, 587 }, suite.ids[targetNodeIndex].NodeID) 588 require.NoError(suite.Suite.T(), err) 589 590 unittest.RequireReturnsBefore(suite.T(), receiveWG.Wait, 1*time.Second, "did not receive message") 591 } 592 593 // TestMultiPing_TwoPings sends two messages concurrently from the first network of the test suit to the last one, 594 // and checks that the last network receives the messages and that the messages are correctly decoded. 595 func (suite *NetworkTestSuite) TestMultiPing_TwoPings() { 596 suite.MultiPing(2) 597 } 598 599 // TestMultiPing_FourPings sends four messages concurrently from the first network of the test suit to the last one, 600 // and checks that the last network receives the messages and that the messages are correctly decoded. 601 func (suite *NetworkTestSuite) TestMultiPing_FourPings() { 602 suite.MultiPing(4) 603 } 604 605 // TestMultiPing_EightPings sends eight messages concurrently from the first network of the test suit to the last one, 606 // and checks that the last network receives the messages and that the messages are correctly decoded. 607 func (suite *NetworkTestSuite) TestMultiPing_EightPings() { 608 suite.MultiPing(8) 609 } 610 611 // MultiPing sends count-many distinct messages concurrently from the first network of the test suit to the last one. 612 // It evaluates the correctness of reception of the content of the messages. Each message must be received by the 613 // last network of the test suit exactly once. 614 func (suite *NetworkTestSuite) MultiPing(count int) { 615 receiveWG := sync.WaitGroup{} 616 sendWG := sync.WaitGroup{} 617 senderNodeIndex := 0 618 targetNodeIndex := suite.size - 1 619 620 receivedPayloads := unittest.NewProtectedMap[string, struct{}]() // keep track of unique payloads received. 621 622 // regex to extract the payload from the message 623 regex := regexp.MustCompile(`^hello from: \d`) 624 625 // creates a conduit on sender to send messages to the target on the test channel. 626 con0, err := suite.networks[senderNodeIndex].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) 627 require.NoError(suite.T(), err) 628 629 // mocks a target engine on the last node of the test suit that will receive the message on the test channel. 630 targetEngine := &mocknetwork.MessageProcessor{} 631 _, err = suite.networks[targetNodeIndex].Register(channels.TestNetworkChannel, targetEngine) 632 targetEngine.On("Process", mockery.Anything, mockery.Anything, mockery.Anything). 633 Run(func(args mockery.Arguments) { 634 receiveWG.Done() 635 636 msgChannel, ok := args[0].(channels.Channel) 637 require.True(suite.T(), ok) 638 require.Equal(suite.T(), channels.TestNetworkChannel, msgChannel) // channel 639 640 msgOriginID, ok := args[1].(flow.Identifier) 641 require.True(suite.T(), ok) 642 require.Equal(suite.T(), suite.ids[senderNodeIndex].NodeID, msgOriginID) // sender id 643 644 msgPayload, ok := args[2].(*libp2pmessage.TestMessage) 645 require.True(suite.T(), ok) 646 // payload 647 require.True(suite.T(), regex.MatchString(msgPayload.Text)) 648 require.False(suite.T(), receivedPayloads.Has(msgPayload.Text)) // payload must be unique 649 receivedPayloads.Add(msgPayload.Text, struct{}{}) 650 }).Return(nil) 651 652 for i := 0; i < count; i++ { 653 receiveWG.Add(1) 654 sendWG.Add(1) 655 656 expectedPayloadText := fmt.Sprintf("hello from: %d", i) 657 require.NoError(suite.T(), err) 658 err = con0.Unicast(&libp2pmessage.TestMessage{ 659 Text: expectedPayloadText, 660 }, suite.ids[targetNodeIndex].NodeID) 661 require.NoError(suite.Suite.T(), err) 662 663 go func() { 664 // sends a direct message from first node to the last node 665 err := con0.Unicast(&libp2pmessage.TestMessage{ 666 Text: expectedPayloadText, 667 }, suite.ids[targetNodeIndex].NodeID) 668 require.NoError(suite.Suite.T(), err) 669 670 sendWG.Done() 671 }() 672 } 673 674 unittest.RequireReturnsBefore(suite.T(), sendWG.Wait, 1*time.Second, "could not send unicasts on time") 675 unittest.RequireReturnsBefore(suite.T(), receiveWG.Wait, 1*time.Second, "could not receive unicasts on time") 676 } 677 678 // TestEcho sends an echo message from first network to the last network 679 // the last network echos back the message. The test evaluates the correctness 680 // of the message reception as well as its content 681 func (suite *NetworkTestSuite) TestEcho() { 682 wg := sync.WaitGroup{} 683 // extracts sender id based on the mock option 684 var err error 685 686 wg.Add(2) 687 first := 0 688 last := suite.size - 1 689 // mocks a target engine on the first and last nodes of the test suit that will receive the message on the test channel. 690 targetEngine1 := &mocknetwork.MessageProcessor{} 691 con1, err := suite.networks[first].Register(channels.TestNetworkChannel, targetEngine1) 692 require.NoError(suite.T(), err) 693 targetEngine2 := &mocknetwork.MessageProcessor{} 694 con2, err := suite.networks[last].Register(channels.TestNetworkChannel, targetEngine2) 695 require.NoError(suite.T(), err) 696 697 // message sent from first node to the last node. 698 expectedSendMsg := "TestEcho" 699 // reply from last node to the first node. 700 expectedReplyMsg := "TestEcho response" 701 702 // mocks the target engine on the last node of the test suit that will receive the message on the test channel, and 703 // echos back the reply message to the sender. 704 targetEngine2.On("Process", mockery.Anything, mockery.Anything, mockery.Anything). 705 Run(func(args mockery.Arguments) { 706 wg.Done() 707 708 msgChannel, ok := args[0].(channels.Channel) 709 require.True(suite.T(), ok) 710 require.Equal(suite.T(), channels.TestNetworkChannel, msgChannel) // channel 711 712 msgOriginID, ok := args[1].(flow.Identifier) 713 require.True(suite.T(), ok) 714 require.Equal(suite.T(), suite.ids[first].NodeID, msgOriginID) // sender id 715 716 msgPayload, ok := args[2].(*libp2pmessage.TestMessage) 717 require.True(suite.T(), ok) 718 require.Equal(suite.T(), expectedSendMsg, msgPayload.Text) // payload 719 720 // echos back the same message back to the sender 721 require.NoError(suite.T(), con2.Unicast(&libp2pmessage.TestMessage{ 722 Text: expectedReplyMsg, 723 }, suite.ids[first].NodeID)) 724 }).Return(nil).Once() 725 726 // mocks the target engine on the last node of the test suit that will receive the message on the test channel, and 727 // echos back the reply message to the sender. 728 targetEngine1.On("Process", mockery.Anything, mockery.Anything, mockery.Anything). 729 Run(func(args mockery.Arguments) { 730 wg.Done() 731 732 msgChannel, ok := args[0].(channels.Channel) 733 require.True(suite.T(), ok) 734 require.Equal(suite.T(), channels.TestNetworkChannel, msgChannel) // channel 735 736 msgOriginID, ok := args[1].(flow.Identifier) 737 require.True(suite.T(), ok) 738 require.Equal(suite.T(), suite.ids[last].NodeID, msgOriginID) // sender id 739 740 msgPayload, ok := args[2].(*libp2pmessage.TestMessage) 741 require.True(suite.T(), ok) 742 require.Equal(suite.T(), expectedReplyMsg, msgPayload.Text) // payload 743 }).Return(nil) 744 745 // sends a direct message from first node to the last node 746 require.NoError(suite.T(), con1.Unicast(&libp2pmessage.TestMessage{ 747 Text: expectedSendMsg, 748 }, suite.ids[last].NodeID)) 749 750 unittest.RequireReturnsBefore(suite.T(), wg.Wait, 5*time.Second, "could not receive unicast on time") 751 } 752 753 // TestMaxMessageSize_Unicast evaluates that invoking Unicast method of the network on a message 754 // size beyond the permissible unicast message size returns an error. 755 func (suite *NetworkTestSuite) TestMaxMessageSize_Unicast() { 756 first := 0 757 last := suite.size - 1 758 759 // creates a network payload beyond the maximum message size 760 // Note: networkPayloadFixture considers 1000 bytes as the overhead of the encoded message, 761 // so the generated payload is 1000 bytes below the maximum unicast message size. 762 // We hence add up 1000 bytes to the input of network payload fixture to make 763 // sure that payload is beyond the permissible size. 764 payload := testutils.NetworkPayloadFixture(suite.T(), uint(underlay.DefaultMaxUnicastMsgSize)+1000) 765 event := &libp2pmessage.TestMessage{ 766 Text: string(payload), 767 } 768 769 // sends a direct message from first node to the last node 770 con0, err := suite.networks[first].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) 771 require.NoError(suite.T(), err) 772 require.Error(suite.T(), con0.Unicast(event, suite.ids[last].NodeID)) 773 } 774 775 // TestLargeMessageSize_SendDirect asserts that a ChunkDataResponse is treated as a large message and can be unicasted 776 // successfully even though it's size is greater than the default message size. 777 func (suite *NetworkTestSuite) TestLargeMessageSize_SendDirect() { 778 sourceIndex := 0 779 targetIndex := suite.size - 1 780 targetId := suite.ids[targetIndex].NodeID 781 782 // creates a network payload with a size greater than the default max size using a known large message type 783 targetSize := uint64(underlay.DefaultMaxUnicastMsgSize) + 1000 784 event := unittest.ChunkDataResponseMsgFixture(unittest.IdentifierFixture(), unittest.WithApproximateSize(targetSize)) 785 786 // expect one message to be received by the target 787 ch := make(chan struct{}) 788 // mocks a target engine on the last node of the test suit that will receive the message on the test channel. 789 targetEngine := &mocknetwork.MessageProcessor{} 790 _, err := suite.networks[targetIndex].Register(channels.ProvideChunks, targetEngine) 791 require.NoError(suite.T(), err) 792 targetEngine.On("Process", mockery.Anything, mockery.Anything, mockery.Anything). 793 Run(func(args mockery.Arguments) { 794 defer close(ch) 795 796 msgChannel, ok := args[0].(channels.Channel) 797 require.True(suite.T(), ok) 798 require.Equal(suite.T(), channels.ProvideChunks, msgChannel) // channel 799 800 msgOriginID, ok := args[1].(flow.Identifier) 801 require.True(suite.T(), ok) 802 require.Equal(suite.T(), suite.ids[sourceIndex].NodeID, msgOriginID) // sender id 803 804 msgPayload, ok := args[2].(*messages.ChunkDataResponse) 805 require.True(suite.T(), ok) 806 require.Equal(suite.T(), event, msgPayload) // payload 807 }).Return(nil).Once() 808 809 // sends a direct message from source node to the target node 810 con0, err := suite.networks[sourceIndex].Register(channels.ProvideChunks, &mocknetwork.MessageProcessor{}) 811 require.NoError(suite.T(), err) 812 require.NoError(suite.T(), con0.Unicast(event, targetId)) 813 814 // check message reception on target 815 unittest.RequireCloseBefore(suite.T(), ch, 5*time.Second, "source node failed to send large message to target") 816 } 817 818 // TestMaxMessageSize_Publish evaluates that invoking Publish method of the network on a message 819 // size beyond the permissible publish message size returns an error. 820 func (suite *NetworkTestSuite) TestMaxMessageSize_Publish() { 821 firstIndex := 0 822 lastIndex := suite.size - 1 823 lastNodeId := suite.ids[lastIndex].NodeID 824 825 // creates a network payload beyond the maximum message size 826 // Note: networkPayloadFixture considers 1000 bytes as the overhead of the encoded message, 827 // so the generated payload is 1000 bytes below the maximum publish message size. 828 // We hence add up 1000 bytes to the input of network payload fixture to make 829 // sure that payload is beyond the permissible size. 830 payload := testutils.NetworkPayloadFixture(suite.T(), uint(p2pnode.DefaultMaxPubSubMsgSize)+1000) 831 event := &libp2pmessage.TestMessage{ 832 Text: string(payload), 833 } 834 con0, err := suite.networks[firstIndex].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) 835 require.NoError(suite.T(), err) 836 837 err = con0.Publish(event, lastNodeId) 838 require.Error(suite.Suite.T(), err) 839 require.ErrorContains(suite.T(), err, "exceeds configured max message size") 840 } 841 842 // TestUnsubscribe tests that an engine can unsubscribe from a topic it was earlier subscribed to and stop receiving 843 // messages. 844 func (suite *NetworkTestSuite) TestUnsubscribe() { 845 senderIndex := 0 846 targetIndex := suite.size - 1 847 targetId := suite.ids[targetIndex].NodeID 848 849 msgRcvd := make(chan struct{}, 2) 850 msgRcvdFun := func() { 851 <-msgRcvd 852 } 853 854 targetEngine := &mocknetwork.MessageProcessor{} 855 con2, err := suite.networks[targetIndex].Register(channels.TestNetworkChannel, targetEngine) 856 require.NoError(suite.T(), err) 857 targetEngine.On("Process", mockery.Anything, mockery.Anything, mockery.Anything). 858 Run(func(args mockery.Arguments) { 859 msgChannel, ok := args[0].(channels.Channel) 860 require.True(suite.T(), ok) 861 require.Equal(suite.T(), channels.TestNetworkChannel, msgChannel) // channel 862 863 msgOriginID, ok := args[1].(flow.Identifier) 864 require.True(suite.T(), ok) 865 require.Equal(suite.T(), suite.ids[senderIndex].NodeID, msgOriginID) // sender id 866 867 msgPayload, ok := args[2].(*libp2pmessage.TestMessage) 868 require.True(suite.T(), ok) 869 require.True(suite.T(), msgPayload.Text == "hello1") // payload, we only expect hello 1 that was sent before unsubscribe. 870 msgRcvd <- struct{}{} 871 }).Return(nil) 872 873 // first test that when both nodes are subscribed to the channel, the target node receives the message 874 con1, err := suite.networks[senderIndex].Register(channels.TestNetworkChannel, &mocknetwork.MessageProcessor{}) 875 require.NoError(suite.T(), err) 876 877 // set up waiting for suite.size pubsub tags indicating a mesh has formed 878 for i := 0; i < suite.size; i++ { 879 select { 880 case <-suite.obs: 881 case <-time.After(2 * time.Second): 882 assert.FailNow(suite.T(), "could not receive pubsub tag indicating mesh formed") 883 } 884 } 885 886 err = con1.Publish(&libp2pmessage.TestMessage{ 887 Text: string("hello1"), 888 }, targetId) 889 require.NoError(suite.T(), err) 890 891 unittest.RequireReturnsBefore(suite.T(), msgRcvdFun, 3*time.Second, "message not received") 892 893 // now unsubscribe the target node from the channel 894 require.NoError(suite.T(), con2.Close()) 895 896 // now publish a new message from the first node 897 err = con1.Publish(&libp2pmessage.TestMessage{ 898 Text: string("hello2"), 899 }, targetId) 900 assert.NoError(suite.T(), err) 901 902 // assert that the new message is not received by the target node 903 unittest.RequireNeverReturnBefore(suite.T(), msgRcvdFun, 2*time.Second, "message received unexpectedly") 904 } 905 906 // TestChunkDataPackMaxMessageSize tests that the max message size for a chunk data pack response is set to the large message size. 907 func TestChunkDataPackMaxMessageSize(t *testing.T) { 908 // creates an outgoing chunk data pack response message (imitating an EN is sending a chunk data pack response to VN). 909 msg, err := message.NewOutgoingScope( 910 flow.IdentifierList{unittest.IdentifierFixture()}, 911 channels.TopicFromChannel(channels.ProvideChunks, unittest.IdentifierFixture()), 912 &messages.ChunkDataResponse{ 913 ChunkDataPack: *unittest.ChunkDataPackFixture(unittest.IdentifierFixture()), 914 Nonce: rand.Uint64(), 915 }, 916 unittest.NetworkCodec().Encode, 917 message.ProtocolTypeUnicast) 918 require.NoError(t, err) 919 920 // get the max message size for the message 921 size, err := underlay.UnicastMaxMsgSizeByCode(msg.Proto().Payload) 922 require.NoError(t, err) 923 require.Equal(t, underlay.LargeMsgMaxUnicastMsgSize, size) 924 }