github.com/MetalBlockchain/metalgo@v1.11.9/snow/networking/sender/sender_test.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package sender 5 6 import ( 7 "context" 8 "math/rand" 9 "sync" 10 "testing" 11 "time" 12 13 "github.com/prometheus/client_golang/prometheus" 14 "github.com/stretchr/testify/require" 15 "go.uber.org/mock/gomock" 16 17 "github.com/MetalBlockchain/metalgo/ids" 18 "github.com/MetalBlockchain/metalgo/message" 19 "github.com/MetalBlockchain/metalgo/network/p2p" 20 "github.com/MetalBlockchain/metalgo/snow" 21 "github.com/MetalBlockchain/metalgo/snow/engine/common" 22 "github.com/MetalBlockchain/metalgo/snow/networking/benchlist" 23 "github.com/MetalBlockchain/metalgo/snow/networking/handler" 24 "github.com/MetalBlockchain/metalgo/snow/networking/router" 25 "github.com/MetalBlockchain/metalgo/snow/networking/timeout" 26 "github.com/MetalBlockchain/metalgo/snow/networking/tracker" 27 "github.com/MetalBlockchain/metalgo/snow/snowtest" 28 "github.com/MetalBlockchain/metalgo/snow/validators" 29 "github.com/MetalBlockchain/metalgo/subnets" 30 "github.com/MetalBlockchain/metalgo/utils/constants" 31 "github.com/MetalBlockchain/metalgo/utils/logging" 32 "github.com/MetalBlockchain/metalgo/utils/math/meter" 33 "github.com/MetalBlockchain/metalgo/utils/resource" 34 "github.com/MetalBlockchain/metalgo/utils/set" 35 "github.com/MetalBlockchain/metalgo/utils/timer" 36 "github.com/MetalBlockchain/metalgo/version" 37 38 p2ppb "github.com/MetalBlockchain/metalgo/proto/pb/p2p" 39 commontracker "github.com/MetalBlockchain/metalgo/snow/engine/common/tracker" 40 ) 41 42 const testThreadPoolSize = 2 43 44 func TestTimeout(t *testing.T) { 45 require := require.New(t) 46 47 snowCtx := snowtest.Context(t, snowtest.CChainID) 48 ctx := snowtest.ConsensusContext(snowCtx) 49 vdrs := validators.NewManager() 50 require.NoError(vdrs.AddStaker(ctx.SubnetID, ids.GenerateTestNodeID(), nil, ids.Empty, 1)) 51 benchlist := benchlist.NewNoBenchlist() 52 tm, err := timeout.NewManager( 53 &timer.AdaptiveTimeoutConfig{ 54 InitialTimeout: time.Millisecond, 55 MinimumTimeout: time.Millisecond, 56 MaximumTimeout: 10 * time.Second, 57 TimeoutHalflife: 5 * time.Minute, 58 TimeoutCoefficient: 1.25, 59 }, 60 benchlist, 61 prometheus.NewRegistry(), 62 prometheus.NewRegistry(), 63 ) 64 require.NoError(err) 65 go tm.Dispatch() 66 67 chainRouter := router.ChainRouter{} 68 69 metrics := prometheus.NewRegistry() 70 mc, err := message.NewCreator( 71 logging.NoLog{}, 72 metrics, 73 constants.DefaultNetworkCompressionType, 74 10*time.Second, 75 ) 76 require.NoError(err) 77 78 require.NoError(chainRouter.Initialize( 79 ids.EmptyNodeID, 80 logging.NoLog{}, 81 tm, 82 time.Second, 83 set.Set[ids.ID]{}, 84 true, 85 set.Set[ids.ID]{}, 86 nil, 87 router.HealthConfig{}, 88 prometheus.NewRegistry(), 89 )) 90 91 externalSender := &ExternalSenderTest{TB: t} 92 externalSender.Default(false) 93 94 sender, err := New( 95 ctx, 96 mc, 97 externalSender, 98 &chainRouter, 99 tm, 100 p2ppb.EngineType_ENGINE_TYPE_SNOWMAN, 101 subnets.New(ctx.NodeID, subnets.Config{}), 102 prometheus.NewRegistry(), 103 ) 104 require.NoError(err) 105 106 ctx2 := snowtest.ConsensusContext(snowCtx) 107 resourceTracker, err := tracker.NewResourceTracker( 108 prometheus.NewRegistry(), 109 resource.NoUsage, 110 meter.ContinuousFactory{}, 111 time.Second, 112 ) 113 require.NoError(err) 114 115 p2pTracker, err := p2p.NewPeerTracker( 116 logging.NoLog{}, 117 "", 118 prometheus.NewRegistry(), 119 nil, 120 version.CurrentApp, 121 ) 122 require.NoError(err) 123 124 h, err := handler.New( 125 ctx2, 126 vdrs, 127 nil, 128 time.Hour, 129 testThreadPoolSize, 130 resourceTracker, 131 validators.UnhandledSubnetConnector, 132 subnets.New(ctx.NodeID, subnets.Config{}), 133 commontracker.NewPeers(), 134 p2pTracker, 135 prometheus.NewRegistry(), 136 ) 137 require.NoError(err) 138 139 bootstrapper := &common.BootstrapperTest{ 140 EngineTest: common.EngineTest{ 141 T: t, 142 }, 143 } 144 bootstrapper.Default(true) 145 bootstrapper.CantGossip = false 146 bootstrapper.ContextF = func() *snow.ConsensusContext { 147 return ctx 148 } 149 bootstrapper.ConnectedF = func(context.Context, ids.NodeID, *version.Application) error { 150 return nil 151 } 152 h.SetEngineManager(&handler.EngineManager{ 153 Avalanche: &handler.Engine{ 154 StateSyncer: nil, 155 Bootstrapper: bootstrapper, 156 Consensus: nil, 157 }, 158 Snowman: &handler.Engine{ 159 StateSyncer: nil, 160 Bootstrapper: bootstrapper, 161 Consensus: nil, 162 }, 163 }) 164 ctx2.State.Set(snow.EngineState{ 165 Type: p2ppb.EngineType_ENGINE_TYPE_SNOWMAN, 166 State: snow.Bootstrapping, // assumed bootstrap is ongoing 167 }) 168 169 chainRouter.AddChain(context.Background(), h) 170 171 bootstrapper.StartF = func(context.Context, uint32) error { 172 return nil 173 } 174 h.Start(context.Background(), false) 175 176 var ( 177 wg = sync.WaitGroup{} 178 vdrIDs = set.Set[ids.NodeID]{} 179 chains = set.Set[ids.ID]{} 180 requestID uint32 181 failedLock sync.Mutex 182 failedVDRs = set.Set[ids.NodeID]{} 183 failedChains = set.Set[ids.ID]{} 184 ) 185 186 cancelledCtx, cancel := context.WithCancel(context.Background()) 187 cancel() 188 189 failed := func(ctx context.Context, nodeID ids.NodeID, _ uint32) error { 190 require.NoError(ctx.Err()) 191 192 failedLock.Lock() 193 defer failedLock.Unlock() 194 195 failedVDRs.Add(nodeID) 196 wg.Done() 197 return nil 198 } 199 200 bootstrapper.GetStateSummaryFrontierFailedF = failed 201 bootstrapper.GetAcceptedStateSummaryFailedF = failed 202 bootstrapper.GetAcceptedFrontierFailedF = failed 203 bootstrapper.GetAcceptedFailedF = failed 204 bootstrapper.GetAncestorsFailedF = failed 205 bootstrapper.GetFailedF = failed 206 bootstrapper.QueryFailedF = failed 207 bootstrapper.AppRequestFailedF = func(ctx context.Context, nodeID ids.NodeID, _ uint32, _ *common.AppError) error { 208 require.NoError(ctx.Err()) 209 210 failedLock.Lock() 211 defer failedLock.Unlock() 212 213 failedVDRs.Add(nodeID) 214 wg.Done() 215 return nil 216 } 217 218 bootstrapper.CrossChainAppRequestFailedF = func(ctx context.Context, chainID ids.ID, _ uint32, _ *common.AppError) error { 219 require.NoError(ctx.Err()) 220 221 failedLock.Lock() 222 defer failedLock.Unlock() 223 224 failedChains.Add(chainID) 225 wg.Done() 226 return nil 227 } 228 229 sendAll := func() { 230 { 231 nodeIDs := set.Of(ids.GenerateTestNodeID()) 232 vdrIDs.Union(nodeIDs) 233 wg.Add(1) 234 requestID++ 235 sender.SendGetStateSummaryFrontier(cancelledCtx, nodeIDs, requestID) 236 } 237 { 238 nodeIDs := set.Of(ids.GenerateTestNodeID()) 239 vdrIDs.Union(nodeIDs) 240 wg.Add(1) 241 requestID++ 242 sender.SendGetAcceptedStateSummary(cancelledCtx, nodeIDs, requestID, nil) 243 } 244 { 245 nodeIDs := set.Of(ids.GenerateTestNodeID()) 246 vdrIDs.Union(nodeIDs) 247 wg.Add(1) 248 requestID++ 249 sender.SendGetAcceptedFrontier(cancelledCtx, nodeIDs, requestID) 250 } 251 { 252 nodeIDs := set.Of(ids.GenerateTestNodeID()) 253 vdrIDs.Union(nodeIDs) 254 wg.Add(1) 255 requestID++ 256 sender.SendGetAccepted(cancelledCtx, nodeIDs, requestID, nil) 257 } 258 { 259 nodeID := ids.GenerateTestNodeID() 260 vdrIDs.Add(nodeID) 261 wg.Add(1) 262 requestID++ 263 sender.SendGetAncestors(cancelledCtx, nodeID, requestID, ids.Empty) 264 } 265 { 266 nodeID := ids.GenerateTestNodeID() 267 vdrIDs.Add(nodeID) 268 wg.Add(1) 269 requestID++ 270 sender.SendGet(cancelledCtx, nodeID, requestID, ids.Empty) 271 } 272 { 273 nodeIDs := set.Of(ids.GenerateTestNodeID()) 274 vdrIDs.Union(nodeIDs) 275 wg.Add(1) 276 requestID++ 277 sender.SendPullQuery(cancelledCtx, nodeIDs, requestID, ids.Empty, 0) 278 } 279 { 280 nodeIDs := set.Of(ids.GenerateTestNodeID()) 281 vdrIDs.Union(nodeIDs) 282 wg.Add(1) 283 requestID++ 284 sender.SendPushQuery(cancelledCtx, nodeIDs, requestID, nil, 0) 285 } 286 { 287 nodeIDs := set.Of(ids.GenerateTestNodeID()) 288 vdrIDs.Union(nodeIDs) 289 wg.Add(1) 290 requestID++ 291 require.NoError(sender.SendAppRequest(cancelledCtx, nodeIDs, requestID, nil)) 292 } 293 { 294 chainID := ids.GenerateTestID() 295 chains.Add(chainID) 296 wg.Add(1) 297 requestID++ 298 require.NoError(sender.SendCrossChainAppRequest(cancelledCtx, chainID, requestID, nil)) 299 } 300 } 301 302 // Send messages to disconnected peers 303 externalSender.SendF = func(message.OutboundMessage, common.SendConfig, ids.ID, subnets.Allower) set.Set[ids.NodeID] { 304 return nil 305 } 306 sendAll() 307 308 // Send messages to connected peers 309 externalSender.SendF = func(_ message.OutboundMessage, config common.SendConfig, _ ids.ID, _ subnets.Allower) set.Set[ids.NodeID] { 310 return config.NodeIDs 311 } 312 sendAll() 313 314 wg.Wait() 315 316 require.Equal(vdrIDs, failedVDRs) 317 require.Equal(chains, failedChains) 318 } 319 320 func TestReliableMessages(t *testing.T) { 321 require := require.New(t) 322 323 snowCtx := snowtest.Context(t, snowtest.CChainID) 324 ctx := snowtest.ConsensusContext(snowCtx) 325 vdrs := validators.NewManager() 326 require.NoError(vdrs.AddStaker(ctx.SubnetID, ids.BuildTestNodeID([]byte{1}), nil, ids.Empty, 1)) 327 benchlist := benchlist.NewNoBenchlist() 328 tm, err := timeout.NewManager( 329 &timer.AdaptiveTimeoutConfig{ 330 InitialTimeout: time.Millisecond, 331 MinimumTimeout: time.Millisecond, 332 MaximumTimeout: time.Millisecond, 333 TimeoutHalflife: 5 * time.Minute, 334 TimeoutCoefficient: 1.25, 335 }, 336 benchlist, 337 prometheus.NewRegistry(), 338 prometheus.NewRegistry(), 339 ) 340 require.NoError(err) 341 342 go tm.Dispatch() 343 344 chainRouter := router.ChainRouter{} 345 346 metrics := prometheus.NewRegistry() 347 mc, err := message.NewCreator( 348 logging.NoLog{}, 349 metrics, 350 constants.DefaultNetworkCompressionType, 351 10*time.Second, 352 ) 353 require.NoError(err) 354 355 require.NoError(chainRouter.Initialize( 356 ids.EmptyNodeID, 357 logging.NoLog{}, 358 tm, 359 time.Second, 360 set.Set[ids.ID]{}, 361 true, 362 set.Set[ids.ID]{}, 363 nil, 364 router.HealthConfig{}, 365 prometheus.NewRegistry(), 366 )) 367 368 externalSender := &ExternalSenderTest{TB: t} 369 externalSender.Default(false) 370 371 sender, err := New( 372 ctx, 373 mc, 374 externalSender, 375 &chainRouter, 376 tm, 377 p2ppb.EngineType_ENGINE_TYPE_SNOWMAN, 378 subnets.New(ctx.NodeID, subnets.Config{}), 379 prometheus.NewRegistry(), 380 ) 381 require.NoError(err) 382 383 ctx2 := snowtest.ConsensusContext(snowCtx) 384 resourceTracker, err := tracker.NewResourceTracker( 385 prometheus.NewRegistry(), 386 resource.NoUsage, 387 meter.ContinuousFactory{}, 388 time.Second, 389 ) 390 require.NoError(err) 391 392 p2pTracker, err := p2p.NewPeerTracker( 393 logging.NoLog{}, 394 "", 395 prometheus.NewRegistry(), 396 nil, 397 version.CurrentApp, 398 ) 399 require.NoError(err) 400 401 h, err := handler.New( 402 ctx2, 403 vdrs, 404 nil, 405 1, 406 testThreadPoolSize, 407 resourceTracker, 408 validators.UnhandledSubnetConnector, 409 subnets.New(ctx.NodeID, subnets.Config{}), 410 commontracker.NewPeers(), 411 p2pTracker, 412 prometheus.NewRegistry(), 413 ) 414 require.NoError(err) 415 416 bootstrapper := &common.BootstrapperTest{ 417 EngineTest: common.EngineTest{ 418 T: t, 419 }, 420 } 421 bootstrapper.Default(true) 422 bootstrapper.CantGossip = false 423 bootstrapper.ContextF = func() *snow.ConsensusContext { 424 return ctx2 425 } 426 bootstrapper.ConnectedF = func(context.Context, ids.NodeID, *version.Application) error { 427 return nil 428 } 429 queriesToSend := 1000 430 awaiting := make([]chan struct{}, queriesToSend) 431 for i := 0; i < queriesToSend; i++ { 432 awaiting[i] = make(chan struct{}, 1) 433 } 434 bootstrapper.QueryFailedF = func(_ context.Context, _ ids.NodeID, reqID uint32) error { 435 close(awaiting[int(reqID)]) 436 return nil 437 } 438 bootstrapper.CantGossip = false 439 h.SetEngineManager(&handler.EngineManager{ 440 Avalanche: &handler.Engine{ 441 StateSyncer: nil, 442 Bootstrapper: bootstrapper, 443 Consensus: nil, 444 }, 445 Snowman: &handler.Engine{ 446 StateSyncer: nil, 447 Bootstrapper: bootstrapper, 448 Consensus: nil, 449 }, 450 }) 451 ctx2.State.Set(snow.EngineState{ 452 Type: p2ppb.EngineType_ENGINE_TYPE_SNOWMAN, 453 State: snow.Bootstrapping, // assumed bootstrap is ongoing 454 }) 455 456 chainRouter.AddChain(context.Background(), h) 457 458 bootstrapper.StartF = func(context.Context, uint32) error { 459 return nil 460 } 461 h.Start(context.Background(), false) 462 463 go func() { 464 for i := 0; i < queriesToSend; i++ { 465 vdrIDs := set.Of(ids.BuildTestNodeID([]byte{1})) 466 467 sender.SendPullQuery(context.Background(), vdrIDs, uint32(i), ids.Empty, 0) 468 time.Sleep(time.Duration(rand.Float64() * float64(time.Microsecond))) // #nosec G404 469 } 470 }() 471 472 for _, await := range awaiting { 473 <-await 474 } 475 } 476 477 func TestReliableMessagesToMyself(t *testing.T) { 478 require := require.New(t) 479 480 benchlist := benchlist.NewNoBenchlist() 481 snowCtx := snowtest.Context(t, snowtest.CChainID) 482 ctx := snowtest.ConsensusContext(snowCtx) 483 vdrs := validators.NewManager() 484 require.NoError(vdrs.AddStaker(ctx.SubnetID, ids.GenerateTestNodeID(), nil, ids.Empty, 1)) 485 tm, err := timeout.NewManager( 486 &timer.AdaptiveTimeoutConfig{ 487 InitialTimeout: 10 * time.Millisecond, 488 MinimumTimeout: 10 * time.Millisecond, 489 MaximumTimeout: 10 * time.Millisecond, // Timeout fires immediately 490 TimeoutHalflife: 5 * time.Minute, 491 TimeoutCoefficient: 1.25, 492 }, 493 benchlist, 494 prometheus.NewRegistry(), 495 prometheus.NewRegistry(), 496 ) 497 require.NoError(err) 498 499 go tm.Dispatch() 500 501 chainRouter := router.ChainRouter{} 502 503 metrics := prometheus.NewRegistry() 504 mc, err := message.NewCreator( 505 logging.NoLog{}, 506 metrics, 507 constants.DefaultNetworkCompressionType, 508 10*time.Second, 509 ) 510 require.NoError(err) 511 512 require.NoError(chainRouter.Initialize( 513 ids.EmptyNodeID, 514 logging.NoLog{}, 515 tm, 516 time.Second, 517 set.Set[ids.ID]{}, 518 true, 519 set.Set[ids.ID]{}, 520 nil, 521 router.HealthConfig{}, 522 prometheus.NewRegistry(), 523 )) 524 525 externalSender := &ExternalSenderTest{TB: t} 526 externalSender.Default(false) 527 528 sender, err := New( 529 ctx, 530 mc, 531 externalSender, 532 &chainRouter, 533 tm, 534 p2ppb.EngineType_ENGINE_TYPE_SNOWMAN, 535 subnets.New(ctx.NodeID, subnets.Config{}), 536 prometheus.NewRegistry(), 537 ) 538 require.NoError(err) 539 540 ctx2 := snowtest.ConsensusContext(snowCtx) 541 resourceTracker, err := tracker.NewResourceTracker( 542 prometheus.NewRegistry(), 543 resource.NoUsage, 544 meter.ContinuousFactory{}, 545 time.Second, 546 ) 547 require.NoError(err) 548 549 p2pTracker, err := p2p.NewPeerTracker( 550 logging.NoLog{}, 551 "", 552 prometheus.NewRegistry(), 553 nil, 554 version.CurrentApp, 555 ) 556 require.NoError(err) 557 558 h, err := handler.New( 559 ctx2, 560 vdrs, 561 nil, 562 time.Second, 563 testThreadPoolSize, 564 resourceTracker, 565 validators.UnhandledSubnetConnector, 566 subnets.New(ctx.NodeID, subnets.Config{}), 567 commontracker.NewPeers(), 568 p2pTracker, 569 prometheus.NewRegistry(), 570 ) 571 require.NoError(err) 572 573 bootstrapper := &common.BootstrapperTest{ 574 EngineTest: common.EngineTest{ 575 T: t, 576 }, 577 } 578 bootstrapper.Default(true) 579 bootstrapper.CantGossip = false 580 bootstrapper.ContextF = func() *snow.ConsensusContext { 581 return ctx2 582 } 583 bootstrapper.ConnectedF = func(context.Context, ids.NodeID, *version.Application) error { 584 return nil 585 } 586 queriesToSend := 2 587 awaiting := make([]chan struct{}, queriesToSend) 588 for i := 0; i < queriesToSend; i++ { 589 awaiting[i] = make(chan struct{}, 1) 590 } 591 bootstrapper.QueryFailedF = func(_ context.Context, _ ids.NodeID, reqID uint32) error { 592 close(awaiting[int(reqID)]) 593 return nil 594 } 595 h.SetEngineManager(&handler.EngineManager{ 596 Avalanche: &handler.Engine{ 597 StateSyncer: nil, 598 Bootstrapper: bootstrapper, 599 Consensus: nil, 600 }, 601 Snowman: &handler.Engine{ 602 StateSyncer: nil, 603 Bootstrapper: bootstrapper, 604 Consensus: nil, 605 }, 606 }) 607 ctx2.State.Set(snow.EngineState{ 608 Type: p2ppb.EngineType_ENGINE_TYPE_SNOWMAN, 609 State: snow.Bootstrapping, // assumed bootstrap is ongoing 610 }) 611 612 chainRouter.AddChain(context.Background(), h) 613 614 bootstrapper.StartF = func(context.Context, uint32) error { 615 return nil 616 } 617 h.Start(context.Background(), false) 618 619 go func() { 620 for i := 0; i < queriesToSend; i++ { 621 // Send a pull query to some random peer that won't respond 622 // because they don't exist. This will almost immediately trigger 623 // a query failed message 624 vdrIDs := set.Of(ids.GenerateTestNodeID()) 625 sender.SendPullQuery(context.Background(), vdrIDs, uint32(i), ids.Empty, 0) 626 } 627 }() 628 629 for _, await := range awaiting { 630 <-await 631 } 632 } 633 634 func TestSender_Bootstrap_Requests(t *testing.T) { 635 var ( 636 successNodeID = ids.GenerateTestNodeID() 637 failedNodeID = ids.GenerateTestNodeID() 638 deadline = time.Second 639 requestID = uint32(1337) 640 heights = []uint64{1, 2, 3} 641 containerIDs = []ids.ID{ids.GenerateTestID(), ids.GenerateTestID()} 642 ) 643 snowCtx := snowtest.Context(t, snowtest.PChainID) 644 ctx := snowtest.ConsensusContext(snowCtx) 645 646 type test struct { 647 name string 648 failedMsgF func(nodeID ids.NodeID) message.InboundMessage 649 assertMsgToMyself func(require *require.Assertions, msg message.InboundMessage) 650 expectedResponseOp message.Op 651 setMsgCreatorExpect func(msgCreator *message.MockOutboundMsgBuilder) 652 setExternalSenderExpect func(externalSender *MockExternalSender) 653 sendF func(require *require.Assertions, sender common.Sender, nodeIDs set.Set[ids.NodeID]) 654 } 655 656 tests := []test{ 657 { 658 name: "GetStateSummaryFrontier", 659 failedMsgF: func(nodeID ids.NodeID) message.InboundMessage { 660 return message.InternalGetStateSummaryFrontierFailed( 661 nodeID, 662 ctx.ChainID, 663 requestID, 664 ) 665 }, 666 assertMsgToMyself: func(require *require.Assertions, msg message.InboundMessage) { 667 require.IsType(&p2ppb.GetStateSummaryFrontier{}, msg.Message()) 668 innerMsg := msg.Message().(*p2ppb.GetStateSummaryFrontier) 669 require.Equal(ctx.ChainID[:], innerMsg.ChainId) 670 require.Equal(requestID, innerMsg.RequestId) 671 require.Equal(uint64(deadline), innerMsg.Deadline) 672 }, 673 expectedResponseOp: message.StateSummaryFrontierOp, 674 setMsgCreatorExpect: func(msgCreator *message.MockOutboundMsgBuilder) { 675 msgCreator.EXPECT().GetStateSummaryFrontier( 676 ctx.ChainID, 677 requestID, 678 deadline, 679 ).Return(nil, nil) 680 }, 681 setExternalSenderExpect: func(externalSender *MockExternalSender) { 682 externalSender.EXPECT().Send( 683 gomock.Any(), // Outbound message 684 common.SendConfig{ 685 // Note [myNodeID] is not in this set 686 NodeIDs: set.Of(successNodeID, failedNodeID), 687 }, 688 ctx.SubnetID, // Subnet ID 689 gomock.Any(), 690 ).Return(set.Of(successNodeID)) 691 }, 692 sendF: func(_ *require.Assertions, sender common.Sender, nodeIDs set.Set[ids.NodeID]) { 693 sender.SendGetStateSummaryFrontier( 694 context.Background(), 695 nodeIDs, 696 requestID, 697 ) 698 }, 699 }, 700 { 701 name: "GetAcceptedStateSummary", 702 failedMsgF: func(nodeID ids.NodeID) message.InboundMessage { 703 return message.InternalGetAcceptedStateSummaryFailed( 704 nodeID, 705 ctx.ChainID, 706 requestID, 707 ) 708 }, 709 assertMsgToMyself: func(require *require.Assertions, msg message.InboundMessage) { 710 require.IsType(&p2ppb.GetAcceptedStateSummary{}, msg.Message()) 711 innerMsg := msg.Message().(*p2ppb.GetAcceptedStateSummary) 712 require.Equal(ctx.ChainID[:], innerMsg.ChainId) 713 require.Equal(requestID, innerMsg.RequestId) 714 require.Equal(uint64(deadline), innerMsg.Deadline) 715 require.Equal(heights, innerMsg.Heights) 716 }, 717 expectedResponseOp: message.AcceptedStateSummaryOp, 718 setMsgCreatorExpect: func(msgCreator *message.MockOutboundMsgBuilder) { 719 msgCreator.EXPECT().GetAcceptedStateSummary( 720 ctx.ChainID, 721 requestID, 722 deadline, 723 heights, 724 ).Return(nil, nil) 725 }, 726 setExternalSenderExpect: func(externalSender *MockExternalSender) { 727 externalSender.EXPECT().Send( 728 gomock.Any(), // Outbound message 729 common.SendConfig{ 730 // Note [myNodeID] is not in this set 731 NodeIDs: set.Of(successNodeID, failedNodeID), 732 }, 733 ctx.SubnetID, // Subnet ID 734 gomock.Any(), 735 ).Return(set.Of(successNodeID)) 736 }, 737 sendF: func(_ *require.Assertions, sender common.Sender, nodeIDs set.Set[ids.NodeID]) { 738 sender.SendGetAcceptedStateSummary(context.Background(), nodeIDs, requestID, heights) 739 }, 740 }, 741 { 742 name: "GetAcceptedFrontier", 743 failedMsgF: func(nodeID ids.NodeID) message.InboundMessage { 744 return message.InternalGetAcceptedFrontierFailed( 745 nodeID, 746 ctx.ChainID, 747 requestID, 748 ) 749 }, 750 assertMsgToMyself: func(require *require.Assertions, msg message.InboundMessage) { 751 require.IsType(&p2ppb.GetAcceptedFrontier{}, msg.Message()) 752 innerMsg := msg.Message().(*p2ppb.GetAcceptedFrontier) 753 require.Equal(ctx.ChainID[:], innerMsg.ChainId) 754 require.Equal(requestID, innerMsg.RequestId) 755 require.Equal(uint64(deadline), innerMsg.Deadline) 756 }, 757 expectedResponseOp: message.AcceptedFrontierOp, 758 setMsgCreatorExpect: func(msgCreator *message.MockOutboundMsgBuilder) { 759 msgCreator.EXPECT().GetAcceptedFrontier( 760 ctx.ChainID, 761 requestID, 762 deadline, 763 ).Return(nil, nil) 764 }, 765 setExternalSenderExpect: func(externalSender *MockExternalSender) { 766 externalSender.EXPECT().Send( 767 gomock.Any(), // Outbound message 768 common.SendConfig{ 769 // Note [myNodeID] is not in this set 770 NodeIDs: set.Of(successNodeID, failedNodeID), 771 }, 772 ctx.SubnetID, // Subnet ID 773 gomock.Any(), 774 ).Return(set.Of(successNodeID)) 775 }, 776 sendF: func(_ *require.Assertions, sender common.Sender, nodeIDs set.Set[ids.NodeID]) { 777 sender.SendGetAcceptedFrontier(context.Background(), nodeIDs, requestID) 778 }, 779 }, 780 { 781 name: "GetAccepted", 782 failedMsgF: func(nodeID ids.NodeID) message.InboundMessage { 783 return message.InternalGetAcceptedFailed( 784 nodeID, 785 ctx.ChainID, 786 requestID, 787 ) 788 }, 789 assertMsgToMyself: func(require *require.Assertions, msg message.InboundMessage) { 790 require.IsType(&p2ppb.GetAccepted{}, msg.Message()) 791 innerMsg := msg.Message().(*p2ppb.GetAccepted) 792 require.Equal(ctx.ChainID[:], innerMsg.ChainId) 793 require.Equal(requestID, innerMsg.RequestId) 794 require.Equal(uint64(deadline), innerMsg.Deadline) 795 }, 796 expectedResponseOp: message.AcceptedOp, 797 setMsgCreatorExpect: func(msgCreator *message.MockOutboundMsgBuilder) { 798 msgCreator.EXPECT().GetAccepted( 799 ctx.ChainID, 800 requestID, 801 deadline, 802 containerIDs, 803 ).Return(nil, nil) 804 }, 805 setExternalSenderExpect: func(externalSender *MockExternalSender) { 806 externalSender.EXPECT().Send( 807 gomock.Any(), // Outbound message 808 common.SendConfig{ 809 // Note [myNodeID] is not in this set 810 NodeIDs: set.Of(successNodeID, failedNodeID), 811 }, 812 ctx.SubnetID, // Subnet ID 813 gomock.Any(), 814 ).Return(set.Of(successNodeID)) 815 }, 816 sendF: func(_ *require.Assertions, sender common.Sender, nodeIDs set.Set[ids.NodeID]) { 817 sender.SendGetAccepted(context.Background(), nodeIDs, requestID, containerIDs) 818 }, 819 }, 820 } 821 822 for _, tt := range tests { 823 t.Run(tt.name, func(t *testing.T) { 824 require := require.New(t) 825 ctrl := gomock.NewController(t) 826 827 var ( 828 msgCreator = message.NewMockOutboundMsgBuilder(ctrl) 829 externalSender = NewMockExternalSender(ctrl) 830 timeoutManager = timeout.NewMockManager(ctrl) 831 router = router.NewMockRouter(ctrl) 832 nodeIDs = set.Of(successNodeID, failedNodeID, ctx.NodeID) 833 nodeIDsCopy set.Set[ids.NodeID] 834 ) 835 nodeIDsCopy.Union(nodeIDs) 836 837 // Instantiate new registerers to avoid duplicate metrics 838 // registration 839 ctx.Registerer = prometheus.NewRegistry() 840 841 sender, err := New( 842 ctx, 843 msgCreator, 844 externalSender, 845 router, 846 timeoutManager, 847 p2ppb.EngineType_ENGINE_TYPE_SNOWMAN, 848 subnets.New(ctx.NodeID, subnets.Config{}), 849 prometheus.NewRegistry(), 850 ) 851 require.NoError(err) 852 853 // Set the timeout (deadline) 854 timeoutManager.EXPECT().TimeoutDuration().Return(deadline).AnyTimes() 855 856 // Make sure we register requests with the router 857 for nodeID := range nodeIDs { 858 expectedFailedMsg := tt.failedMsgF(nodeID) 859 router.EXPECT().RegisterRequest( 860 gomock.Any(), // Context 861 nodeID, // Node ID 862 ctx.ChainID, // Source Chain 863 ctx.ChainID, // Destination Chain 864 requestID, // Request ID 865 tt.expectedResponseOp, // Operation 866 expectedFailedMsg, // Failure Message 867 p2ppb.EngineType_ENGINE_TYPE_UNSPECIFIED, 868 ) 869 } 870 871 // Make sure we send a message to ourselves since [myNodeID] 872 // is in [nodeIDs]. 873 // Note that HandleInbound is called in a separate goroutine 874 // so we need to use a channel to synchronize the test. 875 calledHandleInbound := make(chan struct{}) 876 router.EXPECT().HandleInbound(gomock.Any(), gomock.Any()).Do( 877 func(_ context.Context, msg message.InboundMessage) { 878 // Make sure we're sending ourselves 879 // the expected message. 880 tt.assertMsgToMyself(require, msg) 881 close(calledHandleInbound) 882 }, 883 ) 884 885 // Make sure we're making the correct outbound message. 886 tt.setMsgCreatorExpect(msgCreator) 887 888 // Make sure we're sending the message 889 tt.setExternalSenderExpect(externalSender) 890 891 tt.sendF(require, sender, nodeIDsCopy) 892 893 <-calledHandleInbound 894 }) 895 } 896 } 897 898 func TestSender_Bootstrap_Responses(t *testing.T) { 899 var ( 900 destinationNodeID = ids.GenerateTestNodeID() 901 deadline = time.Second 902 requestID = uint32(1337) 903 summaryIDs = []ids.ID{ids.GenerateTestID(), ids.GenerateTestID()} 904 summary = []byte{1, 2, 3} 905 ) 906 snowCtx := snowtest.Context(t, snowtest.PChainID) 907 ctx := snowtest.ConsensusContext(snowCtx) 908 909 type test struct { 910 name string 911 assertMsgToMyself func(require *require.Assertions, msg message.InboundMessage) 912 setMsgCreatorExpect func(msgCreator *message.MockOutboundMsgBuilder) 913 setExternalSenderExpect func(externalSender *MockExternalSender) 914 sendF func(require *require.Assertions, sender common.Sender, nodeID ids.NodeID) 915 } 916 917 tests := []test{ 918 { 919 name: "StateSummaryFrontier", 920 setMsgCreatorExpect: func(msgCreator *message.MockOutboundMsgBuilder) { 921 msgCreator.EXPECT().StateSummaryFrontier( 922 ctx.ChainID, 923 requestID, 924 summary, 925 ).Return(nil, nil) // Don't care about the message 926 }, 927 assertMsgToMyself: func(require *require.Assertions, msg message.InboundMessage) { 928 require.IsType(&p2ppb.StateSummaryFrontier{}, msg.Message()) 929 innerMsg := msg.Message().(*p2ppb.StateSummaryFrontier) 930 require.Equal(ctx.ChainID[:], innerMsg.ChainId) 931 require.Equal(requestID, innerMsg.RequestId) 932 require.Equal(summary, innerMsg.Summary) 933 }, 934 setExternalSenderExpect: func(externalSender *MockExternalSender) { 935 externalSender.EXPECT().Send( 936 gomock.Any(), // Outbound message 937 common.SendConfig{ 938 NodeIDs: set.Of(destinationNodeID), 939 }, 940 ctx.SubnetID, // Subnet ID 941 gomock.Any(), 942 ).Return(nil) 943 }, 944 sendF: func(_ *require.Assertions, sender common.Sender, nodeID ids.NodeID) { 945 sender.SendStateSummaryFrontier(context.Background(), nodeID, requestID, summary) 946 }, 947 }, 948 { 949 name: "AcceptedStateSummary", 950 setMsgCreatorExpect: func(msgCreator *message.MockOutboundMsgBuilder) { 951 msgCreator.EXPECT().AcceptedStateSummary( 952 ctx.ChainID, 953 requestID, 954 summaryIDs, 955 ).Return(nil, nil) // Don't care about the message 956 }, 957 assertMsgToMyself: func(require *require.Assertions, msg message.InboundMessage) { 958 require.IsType(&p2ppb.AcceptedStateSummary{}, msg.Message()) 959 innerMsg := msg.Message().(*p2ppb.AcceptedStateSummary) 960 require.Equal(ctx.ChainID[:], innerMsg.ChainId) 961 require.Equal(requestID, innerMsg.RequestId) 962 for i, summaryID := range summaryIDs { 963 require.Equal(summaryID[:], innerMsg.SummaryIds[i]) 964 } 965 }, 966 setExternalSenderExpect: func(externalSender *MockExternalSender) { 967 externalSender.EXPECT().Send( 968 gomock.Any(), // Outbound message 969 common.SendConfig{ 970 NodeIDs: set.Of(destinationNodeID), 971 }, 972 ctx.SubnetID, // Subnet ID 973 gomock.Any(), 974 ).Return(nil) 975 }, 976 sendF: func(_ *require.Assertions, sender common.Sender, nodeID ids.NodeID) { 977 sender.SendAcceptedStateSummary(context.Background(), nodeID, requestID, summaryIDs) 978 }, 979 }, 980 { 981 name: "AcceptedFrontier", 982 setMsgCreatorExpect: func(msgCreator *message.MockOutboundMsgBuilder) { 983 msgCreator.EXPECT().AcceptedFrontier( 984 ctx.ChainID, 985 requestID, 986 summaryIDs[0], 987 ).Return(nil, nil) // Don't care about the message 988 }, 989 assertMsgToMyself: func(require *require.Assertions, msg message.InboundMessage) { 990 require.IsType(&p2ppb.AcceptedFrontier{}, msg.Message()) 991 innerMsg := msg.Message().(*p2ppb.AcceptedFrontier) 992 require.Equal(ctx.ChainID[:], innerMsg.ChainId) 993 require.Equal(requestID, innerMsg.RequestId) 994 require.Equal(summaryIDs[0][:], innerMsg.ContainerId) 995 }, 996 setExternalSenderExpect: func(externalSender *MockExternalSender) { 997 externalSender.EXPECT().Send( 998 gomock.Any(), // Outbound message 999 common.SendConfig{ 1000 NodeIDs: set.Of(destinationNodeID), 1001 }, 1002 ctx.SubnetID, // Subnet ID 1003 gomock.Any(), 1004 ).Return(nil) 1005 }, 1006 sendF: func(_ *require.Assertions, sender common.Sender, nodeID ids.NodeID) { 1007 sender.SendAcceptedFrontier(context.Background(), nodeID, requestID, summaryIDs[0]) 1008 }, 1009 }, 1010 { 1011 name: "Accepted", 1012 setMsgCreatorExpect: func(msgCreator *message.MockOutboundMsgBuilder) { 1013 msgCreator.EXPECT().Accepted( 1014 ctx.ChainID, 1015 requestID, 1016 summaryIDs, 1017 ).Return(nil, nil) // Don't care about the message 1018 }, 1019 assertMsgToMyself: func(require *require.Assertions, msg message.InboundMessage) { 1020 require.IsType(&p2ppb.Accepted{}, msg.Message()) 1021 innerMsg := msg.Message().(*p2ppb.Accepted) 1022 require.Equal(ctx.ChainID[:], innerMsg.ChainId) 1023 require.Equal(requestID, innerMsg.RequestId) 1024 for i, summaryID := range summaryIDs { 1025 require.Equal(summaryID[:], innerMsg.ContainerIds[i]) 1026 } 1027 }, 1028 setExternalSenderExpect: func(externalSender *MockExternalSender) { 1029 externalSender.EXPECT().Send( 1030 gomock.Any(), // Outbound message 1031 common.SendConfig{ 1032 NodeIDs: set.Of(destinationNodeID), 1033 }, 1034 ctx.SubnetID, // Subnet ID 1035 gomock.Any(), 1036 ).Return(nil) 1037 }, 1038 sendF: func(_ *require.Assertions, sender common.Sender, nodeID ids.NodeID) { 1039 sender.SendAccepted(context.Background(), nodeID, requestID, summaryIDs) 1040 }, 1041 }, 1042 } 1043 1044 for _, tt := range tests { 1045 t.Run(tt.name, func(t *testing.T) { 1046 require := require.New(t) 1047 ctrl := gomock.NewController(t) 1048 1049 var ( 1050 msgCreator = message.NewMockOutboundMsgBuilder(ctrl) 1051 externalSender = NewMockExternalSender(ctrl) 1052 timeoutManager = timeout.NewMockManager(ctrl) 1053 router = router.NewMockRouter(ctrl) 1054 ) 1055 1056 sender, err := New( 1057 ctx, 1058 msgCreator, 1059 externalSender, 1060 router, 1061 timeoutManager, 1062 p2ppb.EngineType_ENGINE_TYPE_SNOWMAN, 1063 subnets.New(ctx.NodeID, subnets.Config{}), 1064 prometheus.NewRegistry(), 1065 ) 1066 require.NoError(err) 1067 1068 // Set the timeout (deadline) 1069 timeoutManager.EXPECT().TimeoutDuration().Return(deadline).AnyTimes() 1070 1071 // Case: sending to ourselves 1072 { 1073 calledHandleInbound := make(chan struct{}) 1074 router.EXPECT().HandleInbound(gomock.Any(), gomock.Any()).Do( 1075 func(_ context.Context, msg message.InboundMessage) { 1076 // Make sure we're sending ourselves 1077 // the expected message. 1078 tt.assertMsgToMyself(require, msg) 1079 close(calledHandleInbound) 1080 }, 1081 ) 1082 tt.sendF(require, sender, ctx.NodeID) 1083 <-calledHandleInbound 1084 } 1085 1086 // Case: not sending to ourselves 1087 1088 // Make sure we're making the correct outbound message. 1089 tt.setMsgCreatorExpect(msgCreator) 1090 1091 // Make sure we're sending the message 1092 tt.setExternalSenderExpect(externalSender) 1093 1094 tt.sendF(require, sender, destinationNodeID) 1095 }) 1096 } 1097 } 1098 1099 func TestSender_Single_Request(t *testing.T) { 1100 var ( 1101 destinationNodeID = ids.GenerateTestNodeID() 1102 deadline = time.Second 1103 requestID = uint32(1337) 1104 containerID = ids.GenerateTestID() 1105 engineType = p2ppb.EngineType_ENGINE_TYPE_SNOWMAN 1106 ) 1107 snowCtx := snowtest.Context(t, snowtest.PChainID) 1108 ctx := snowtest.ConsensusContext(snowCtx) 1109 1110 type test struct { 1111 name string 1112 failedMsgF func(nodeID ids.NodeID) message.InboundMessage 1113 shouldFailMessageToSelf bool 1114 assertMsg func(require *require.Assertions, msg message.InboundMessage) 1115 expectedResponseOp message.Op 1116 setMsgCreatorExpect func(msgCreator *message.MockOutboundMsgBuilder) 1117 setExternalSenderExpect func(externalSender *MockExternalSender, sentTo set.Set[ids.NodeID]) 1118 sendF func(require *require.Assertions, sender common.Sender, nodeID ids.NodeID) 1119 expectedEngineType p2ppb.EngineType 1120 } 1121 1122 tests := []test{ 1123 { 1124 name: "GetAncestors", 1125 failedMsgF: func(nodeID ids.NodeID) message.InboundMessage { 1126 return message.InternalGetAncestorsFailed( 1127 nodeID, 1128 ctx.ChainID, 1129 requestID, 1130 engineType, 1131 ) 1132 }, 1133 shouldFailMessageToSelf: false, 1134 assertMsg: func(require *require.Assertions, msg message.InboundMessage) { 1135 require.IsType(&message.GetAncestorsFailed{}, msg.Message()) 1136 innerMsg := msg.Message().(*message.GetAncestorsFailed) 1137 require.Equal(ctx.ChainID, innerMsg.ChainID) 1138 require.Equal(requestID, innerMsg.RequestID) 1139 require.Equal(engineType, innerMsg.EngineType) 1140 }, 1141 expectedResponseOp: message.AncestorsOp, 1142 setMsgCreatorExpect: func(msgCreator *message.MockOutboundMsgBuilder) { 1143 msgCreator.EXPECT().GetAncestors( 1144 ctx.ChainID, 1145 requestID, 1146 deadline, 1147 containerID, 1148 engineType, 1149 ).Return(nil, nil) 1150 }, 1151 setExternalSenderExpect: func(externalSender *MockExternalSender, sentTo set.Set[ids.NodeID]) { 1152 externalSender.EXPECT().Send( 1153 gomock.Any(), // Outbound message 1154 common.SendConfig{ 1155 NodeIDs: set.Of(destinationNodeID), 1156 }, 1157 ctx.SubnetID, 1158 gomock.Any(), 1159 ).Return(sentTo) 1160 }, 1161 sendF: func(_ *require.Assertions, sender common.Sender, nodeID ids.NodeID) { 1162 sender.SendGetAncestors(context.Background(), nodeID, requestID, containerID) 1163 }, 1164 expectedEngineType: engineType, 1165 }, 1166 { 1167 name: "Get", 1168 failedMsgF: func(nodeID ids.NodeID) message.InboundMessage { 1169 return message.InternalGetFailed( 1170 nodeID, 1171 ctx.ChainID, 1172 requestID, 1173 ) 1174 }, 1175 shouldFailMessageToSelf: true, 1176 assertMsg: func(require *require.Assertions, msg message.InboundMessage) { 1177 require.IsType(&message.GetFailed{}, msg.Message()) 1178 innerMsg := msg.Message().(*message.GetFailed) 1179 require.Equal(ctx.ChainID, innerMsg.ChainID) 1180 require.Equal(requestID, innerMsg.RequestID) 1181 }, 1182 expectedResponseOp: message.PutOp, 1183 setMsgCreatorExpect: func(msgCreator *message.MockOutboundMsgBuilder) { 1184 msgCreator.EXPECT().Get( 1185 ctx.ChainID, 1186 requestID, 1187 deadline, 1188 containerID, 1189 ).Return(nil, nil) 1190 }, 1191 setExternalSenderExpect: func(externalSender *MockExternalSender, sentTo set.Set[ids.NodeID]) { 1192 externalSender.EXPECT().Send( 1193 gomock.Any(), // Outbound message 1194 common.SendConfig{ 1195 NodeIDs: set.Of(destinationNodeID), 1196 }, 1197 ctx.SubnetID, 1198 gomock.Any(), 1199 ).Return(sentTo) 1200 }, 1201 sendF: func(_ *require.Assertions, sender common.Sender, nodeID ids.NodeID) { 1202 sender.SendGet(context.Background(), nodeID, requestID, containerID) 1203 }, 1204 }, 1205 } 1206 1207 for _, tt := range tests { 1208 t.Run(tt.name, func(t *testing.T) { 1209 require := require.New(t) 1210 ctrl := gomock.NewController(t) 1211 1212 var ( 1213 msgCreator = message.NewMockOutboundMsgBuilder(ctrl) 1214 externalSender = NewMockExternalSender(ctrl) 1215 timeoutManager = timeout.NewMockManager(ctrl) 1216 router = router.NewMockRouter(ctrl) 1217 ) 1218 1219 // Instantiate new registerers to avoid duplicate metrics 1220 // registration 1221 ctx.Registerer = prometheus.NewRegistry() 1222 1223 sender, err := New( 1224 ctx, 1225 msgCreator, 1226 externalSender, 1227 router, 1228 timeoutManager, 1229 engineType, 1230 subnets.New(ctx.NodeID, subnets.Config{}), 1231 prometheus.NewRegistry(), 1232 ) 1233 require.NoError(err) 1234 1235 // Set the timeout (deadline) 1236 timeoutManager.EXPECT().TimeoutDuration().Return(deadline).AnyTimes() 1237 1238 // Case: sending to myself 1239 { 1240 // Make sure we register requests with the router 1241 expectedFailedMsg := tt.failedMsgF(ctx.NodeID) 1242 router.EXPECT().RegisterRequest( 1243 gomock.Any(), // Context 1244 ctx.NodeID, // Node ID 1245 ctx.ChainID, // Source Chain 1246 ctx.ChainID, // Destination Chain 1247 requestID, // Request ID 1248 tt.expectedResponseOp, // Operation 1249 expectedFailedMsg, // Failure Message 1250 tt.expectedEngineType, // Engine Type 1251 ) 1252 1253 // Note that HandleInbound is called in a separate goroutine 1254 // so we need to use a channel to synchronize the test. 1255 calledHandleInbound := make(chan struct{}) 1256 if tt.shouldFailMessageToSelf { 1257 router.EXPECT().HandleInbound(gomock.Any(), gomock.Any()).Do( 1258 func(_ context.Context, msg message.InboundMessage) { 1259 // Make sure we're sending ourselves 1260 // the expected message. 1261 tt.assertMsg(require, msg) 1262 close(calledHandleInbound) 1263 }, 1264 ) 1265 } else { 1266 close(calledHandleInbound) 1267 } 1268 1269 tt.sendF(require, sender, ctx.NodeID) 1270 1271 <-calledHandleInbound 1272 } 1273 1274 // Case: Node is benched 1275 { 1276 timeoutManager.EXPECT().IsBenched(destinationNodeID, ctx.ChainID).Return(true) 1277 1278 timeoutManager.EXPECT().RegisterRequestToUnreachableValidator() 1279 1280 // Make sure we register requests with the router 1281 expectedFailedMsg := tt.failedMsgF(destinationNodeID) 1282 router.EXPECT().RegisterRequest( 1283 gomock.Any(), // Context 1284 destinationNodeID, // Node ID 1285 ctx.ChainID, // Source Chain 1286 ctx.ChainID, // Destination Chain 1287 requestID, // Request ID 1288 tt.expectedResponseOp, // Operation 1289 expectedFailedMsg, // Failure Message 1290 tt.expectedEngineType, // Engine Type 1291 ) 1292 1293 // Note that HandleInbound is called in a separate goroutine 1294 // so we need to use a channel to synchronize the test. 1295 calledHandleInbound := make(chan struct{}) 1296 router.EXPECT().HandleInbound(gomock.Any(), gomock.Any()).Do( 1297 func(_ context.Context, msg message.InboundMessage) { 1298 // Make sure we're sending ourselves 1299 // the expected message. 1300 tt.assertMsg(require, msg) 1301 close(calledHandleInbound) 1302 }, 1303 ) 1304 1305 tt.sendF(require, sender, destinationNodeID) 1306 1307 <-calledHandleInbound 1308 } 1309 1310 // Case: Node is not myself, not benched and send fails 1311 { 1312 timeoutManager.EXPECT().IsBenched(destinationNodeID, ctx.ChainID).Return(false) 1313 1314 timeoutManager.EXPECT().RegisterRequestToUnreachableValidator() 1315 1316 // Make sure we register requests with the router 1317 expectedFailedMsg := tt.failedMsgF(destinationNodeID) 1318 router.EXPECT().RegisterRequest( 1319 gomock.Any(), // Context 1320 destinationNodeID, // Node ID 1321 ctx.ChainID, // Source Chain 1322 ctx.ChainID, // Destination Chain 1323 requestID, // Request ID 1324 tt.expectedResponseOp, // Operation 1325 expectedFailedMsg, // Failure Message 1326 tt.expectedEngineType, // Engine Type 1327 ) 1328 1329 // Note that HandleInbound is called in a separate goroutine 1330 // so we need to use a channel to synchronize the test. 1331 calledHandleInbound := make(chan struct{}) 1332 router.EXPECT().HandleInbound(gomock.Any(), gomock.Any()).Do( 1333 func(_ context.Context, msg message.InboundMessage) { 1334 // Make sure we're sending ourselves 1335 // the expected message. 1336 tt.assertMsg(require, msg) 1337 close(calledHandleInbound) 1338 }, 1339 ) 1340 1341 // Make sure we're making the correct outbound message. 1342 tt.setMsgCreatorExpect(msgCreator) 1343 1344 // Make sure we're sending the message 1345 tt.setExternalSenderExpect(externalSender, set.Set[ids.NodeID]{}) 1346 1347 tt.sendF(require, sender, destinationNodeID) 1348 1349 <-calledHandleInbound 1350 } 1351 }) 1352 } 1353 }