github.com/MetalBlockchain/metalgo@v1.11.9/message/messages_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 message 5 6 import ( 7 "bytes" 8 "net" 9 "testing" 10 "time" 11 12 "github.com/prometheus/client_golang/prometheus" 13 "github.com/stretchr/testify/require" 14 "google.golang.org/protobuf/proto" 15 16 "github.com/MetalBlockchain/metalgo/ids" 17 "github.com/MetalBlockchain/metalgo/proto/pb/p2p" 18 "github.com/MetalBlockchain/metalgo/staking" 19 "github.com/MetalBlockchain/metalgo/utils/compression" 20 "github.com/MetalBlockchain/metalgo/utils/logging" 21 ) 22 23 func TestMessage(t *testing.T) { 24 t.Parallel() 25 26 mb, err := newMsgBuilder( 27 logging.NoLog{}, 28 prometheus.NewRegistry(), 29 5*time.Second, 30 ) 31 require.NoError(t, err) 32 33 testID := ids.GenerateTestID() 34 compressibleContainers := [][]byte{ 35 bytes.Repeat([]byte{0}, 100), 36 bytes.Repeat([]byte{0}, 32), 37 bytes.Repeat([]byte{0}, 32), 38 } 39 40 testCertRaw, testKeyRaw, err := staking.NewCertAndKeyBytes() 41 require.NoError(t, err) 42 43 testTLSCert, err := staking.LoadTLSCertFromBytes(testKeyRaw, testCertRaw) 44 require.NoError(t, err) 45 46 nowUnix := time.Now().Unix() 47 48 tests := []struct { 49 desc string 50 op Op 51 msg *p2p.Message 52 compressionType compression.Type 53 bypassThrottling bool 54 bytesSaved bool // if true, outbound message saved bytes must be non-zero 55 }{ 56 { 57 desc: "ping message with no compression no subnet uptimes", 58 op: PingOp, 59 msg: &p2p.Message{ 60 Message: &p2p.Message_Ping{ 61 Ping: &p2p.Ping{}, 62 }, 63 }, 64 compressionType: compression.TypeNone, 65 bypassThrottling: true, 66 bytesSaved: false, 67 }, 68 { 69 desc: "pong message with no compression", 70 op: PongOp, 71 msg: &p2p.Message{ 72 Message: &p2p.Message_Pong{ 73 Pong: &p2p.Pong{}, 74 }, 75 }, 76 compressionType: compression.TypeNone, 77 bypassThrottling: true, 78 bytesSaved: false, 79 }, 80 { 81 desc: "ping message with no compression and subnet uptimes", 82 op: PingOp, 83 msg: &p2p.Message{ 84 Message: &p2p.Message_Ping{ 85 Ping: &p2p.Ping{ 86 SubnetUptimes: []*p2p.SubnetUptime{ 87 { 88 SubnetId: testID[:], 89 Uptime: 100, 90 }, 91 }, 92 }, 93 }, 94 }, 95 compressionType: compression.TypeNone, 96 bypassThrottling: true, 97 bytesSaved: false, 98 }, 99 { 100 desc: "Handshake message with no compression", 101 op: HandshakeOp, 102 msg: &p2p.Message{ 103 Message: &p2p.Message_Handshake{ 104 Handshake: &p2p.Handshake{ 105 NetworkId: uint32(1337), 106 MyTime: uint64(nowUnix), 107 IpAddr: []byte(net.IPv6zero), 108 IpPort: 9651, 109 IpSigningTime: uint64(nowUnix), 110 IpNodeIdSig: []byte{'y', 'e', 'e', 't'}, 111 TrackedSubnets: [][]byte{testID[:]}, 112 IpBlsSig: []byte{'y', 'e', 'e', 't', '2'}, 113 }, 114 }, 115 }, 116 compressionType: compression.TypeNone, 117 bypassThrottling: true, 118 bytesSaved: false, 119 }, 120 { 121 desc: "get_peer_list message with no compression", 122 op: GetPeerListOp, 123 msg: &p2p.Message{ 124 Message: &p2p.Message_GetPeerList{ 125 GetPeerList: &p2p.GetPeerList{ 126 KnownPeers: &p2p.BloomFilter{ 127 Filter: make([]byte, 2048), 128 Salt: make([]byte, 32), 129 }, 130 }, 131 }, 132 }, 133 compressionType: compression.TypeNone, 134 bypassThrottling: false, 135 bytesSaved: false, 136 }, 137 { 138 desc: "get_peer_list message with zstd compression", 139 op: GetPeerListOp, 140 msg: &p2p.Message{ 141 Message: &p2p.Message_GetPeerList{ 142 GetPeerList: &p2p.GetPeerList{ 143 KnownPeers: &p2p.BloomFilter{ 144 Filter: make([]byte, 2048), 145 Salt: make([]byte, 32), 146 }, 147 }, 148 }, 149 }, 150 compressionType: compression.TypeZstd, 151 bypassThrottling: false, 152 bytesSaved: true, 153 }, 154 { 155 desc: "peer_list message with no compression", 156 op: PeerListOp, 157 msg: &p2p.Message{ 158 Message: &p2p.Message_PeerList_{ 159 PeerList_: &p2p.PeerList{ 160 ClaimedIpPorts: []*p2p.ClaimedIpPort{ 161 { 162 X509Certificate: testTLSCert.Certificate[0], 163 IpAddr: []byte(net.IPv4zero), 164 IpPort: 10, 165 Timestamp: 1, 166 Signature: []byte{0}, 167 }, 168 }, 169 }, 170 }, 171 }, 172 compressionType: compression.TypeNone, 173 bypassThrottling: true, 174 bytesSaved: false, 175 }, 176 { 177 desc: "peer_list message with zstd compression", 178 op: PeerListOp, 179 msg: &p2p.Message{ 180 Message: &p2p.Message_PeerList_{ 181 PeerList_: &p2p.PeerList{ 182 ClaimedIpPorts: []*p2p.ClaimedIpPort{ 183 { 184 X509Certificate: testTLSCert.Certificate[0], 185 IpAddr: []byte(net.IPv6zero), 186 IpPort: 9651, 187 Timestamp: uint64(nowUnix), 188 Signature: compressibleContainers[0], 189 }, 190 }, 191 }, 192 }, 193 }, 194 compressionType: compression.TypeZstd, 195 bypassThrottling: true, 196 bytesSaved: true, 197 }, 198 { 199 desc: "get_state_summary_frontier message with no compression", 200 op: GetStateSummaryFrontierOp, 201 msg: &p2p.Message{ 202 Message: &p2p.Message_GetStateSummaryFrontier{ 203 GetStateSummaryFrontier: &p2p.GetStateSummaryFrontier{ 204 ChainId: testID[:], 205 RequestId: 1, 206 Deadline: 1, 207 }, 208 }, 209 }, 210 compressionType: compression.TypeNone, 211 bypassThrottling: true, 212 bytesSaved: false, 213 }, 214 { 215 desc: "state_summary_frontier message with no compression", 216 op: StateSummaryFrontierOp, 217 msg: &p2p.Message{ 218 Message: &p2p.Message_StateSummaryFrontier_{ 219 StateSummaryFrontier_: &p2p.StateSummaryFrontier{ 220 ChainId: testID[:], 221 RequestId: 1, 222 Summary: []byte{0}, 223 }, 224 }, 225 }, 226 compressionType: compression.TypeNone, 227 bypassThrottling: true, 228 bytesSaved: false, 229 }, 230 { 231 desc: "state_summary_frontier message with zstd compression", 232 op: StateSummaryFrontierOp, 233 msg: &p2p.Message{ 234 Message: &p2p.Message_StateSummaryFrontier_{ 235 StateSummaryFrontier_: &p2p.StateSummaryFrontier{ 236 ChainId: testID[:], 237 RequestId: 1, 238 Summary: compressibleContainers[0], 239 }, 240 }, 241 }, 242 compressionType: compression.TypeZstd, 243 bypassThrottling: true, 244 bytesSaved: true, 245 }, 246 { 247 desc: "get_accepted_state_summary message with no compression", 248 op: GetAcceptedStateSummaryOp, 249 msg: &p2p.Message{ 250 Message: &p2p.Message_GetAcceptedStateSummary{ 251 GetAcceptedStateSummary: &p2p.GetAcceptedStateSummary{ 252 ChainId: testID[:], 253 RequestId: 1, 254 Deadline: 1, 255 Heights: []uint64{0}, 256 }, 257 }, 258 }, 259 compressionType: compression.TypeNone, 260 bypassThrottling: true, 261 bytesSaved: false, 262 }, 263 { 264 desc: "get_accepted_state_summary message with zstd compression", 265 op: GetAcceptedStateSummaryOp, 266 msg: &p2p.Message{ 267 Message: &p2p.Message_GetAcceptedStateSummary{ 268 GetAcceptedStateSummary: &p2p.GetAcceptedStateSummary{ 269 ChainId: testID[:], 270 RequestId: 1, 271 Deadline: 1, 272 Heights: []uint64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 273 }, 274 }, 275 }, 276 compressionType: compression.TypeZstd, 277 bypassThrottling: true, 278 bytesSaved: true, 279 }, 280 { 281 desc: "accepted_state_summary message with no compression", 282 op: AcceptedStateSummaryOp, 283 msg: &p2p.Message{ 284 Message: &p2p.Message_AcceptedStateSummary_{ 285 AcceptedStateSummary_: &p2p.AcceptedStateSummary{ 286 ChainId: testID[:], 287 RequestId: 1, 288 SummaryIds: [][]byte{testID[:], testID[:]}, 289 }, 290 }, 291 }, 292 compressionType: compression.TypeNone, 293 bypassThrottling: true, 294 bytesSaved: false, 295 }, 296 { 297 desc: "accepted_state_summary message with zstd compression", 298 op: AcceptedStateSummaryOp, 299 msg: &p2p.Message{ 300 Message: &p2p.Message_AcceptedStateSummary_{ 301 AcceptedStateSummary_: &p2p.AcceptedStateSummary{ 302 ChainId: testID[:], 303 RequestId: 1, 304 SummaryIds: [][]byte{testID[:], testID[:], testID[:], testID[:], testID[:], testID[:], testID[:], testID[:], testID[:]}, 305 }, 306 }, 307 }, 308 compressionType: compression.TypeZstd, 309 bypassThrottling: true, 310 bytesSaved: true, 311 }, 312 { 313 desc: "get_accepted_frontier message with no compression", 314 op: GetAcceptedFrontierOp, 315 msg: &p2p.Message{ 316 Message: &p2p.Message_GetAcceptedFrontier{ 317 GetAcceptedFrontier: &p2p.GetAcceptedFrontier{ 318 ChainId: testID[:], 319 RequestId: 1, 320 Deadline: 1, 321 }, 322 }, 323 }, 324 compressionType: compression.TypeNone, 325 bypassThrottling: true, 326 bytesSaved: false, 327 }, 328 { 329 desc: "accepted_frontier message with no compression", 330 op: AcceptedFrontierOp, 331 msg: &p2p.Message{ 332 Message: &p2p.Message_AcceptedFrontier_{ 333 AcceptedFrontier_: &p2p.AcceptedFrontier{ 334 ChainId: testID[:], 335 RequestId: 1, 336 ContainerId: testID[:], 337 }, 338 }, 339 }, 340 compressionType: compression.TypeNone, 341 bypassThrottling: true, 342 bytesSaved: false, 343 }, 344 { 345 desc: "get_accepted message with no compression", 346 op: GetAcceptedOp, 347 msg: &p2p.Message{ 348 Message: &p2p.Message_GetAccepted{ 349 GetAccepted: &p2p.GetAccepted{ 350 ChainId: testID[:], 351 RequestId: 1, 352 Deadline: 1, 353 ContainerIds: [][]byte{testID[:], testID[:]}, 354 }, 355 }, 356 }, 357 compressionType: compression.TypeNone, 358 bypassThrottling: true, 359 bytesSaved: false, 360 }, 361 { 362 desc: "accepted message with no compression", 363 op: AcceptedOp, 364 msg: &p2p.Message{ 365 Message: &p2p.Message_Accepted_{ 366 Accepted_: &p2p.Accepted{ 367 ChainId: testID[:], 368 RequestId: 1, 369 ContainerIds: [][]byte{testID[:], testID[:]}, 370 }, 371 }, 372 }, 373 compressionType: compression.TypeNone, 374 bypassThrottling: true, 375 bytesSaved: false, 376 }, 377 { 378 desc: "get_ancestors message with no compression", 379 op: GetAncestorsOp, 380 msg: &p2p.Message{ 381 Message: &p2p.Message_GetAncestors{ 382 GetAncestors: &p2p.GetAncestors{ 383 ChainId: testID[:], 384 RequestId: 1, 385 Deadline: 1, 386 ContainerId: testID[:], 387 EngineType: p2p.EngineType_ENGINE_TYPE_AVALANCHE, 388 }, 389 }, 390 }, 391 compressionType: compression.TypeNone, 392 bypassThrottling: true, 393 bytesSaved: false, 394 }, 395 { 396 desc: "ancestors message with no compression", 397 op: AncestorsOp, 398 msg: &p2p.Message{ 399 Message: &p2p.Message_Ancestors_{ 400 Ancestors_: &p2p.Ancestors{ 401 ChainId: testID[:], 402 RequestId: 12345, 403 Containers: compressibleContainers, 404 }, 405 }, 406 }, 407 compressionType: compression.TypeNone, 408 bypassThrottling: true, 409 bytesSaved: false, 410 }, 411 { 412 desc: "ancestors message with zstd compression", 413 op: AncestorsOp, 414 msg: &p2p.Message{ 415 Message: &p2p.Message_Ancestors_{ 416 Ancestors_: &p2p.Ancestors{ 417 ChainId: testID[:], 418 RequestId: 12345, 419 Containers: compressibleContainers, 420 }, 421 }, 422 }, 423 compressionType: compression.TypeZstd, 424 bypassThrottling: true, 425 bytesSaved: true, 426 }, 427 { 428 desc: "get message with no compression", 429 op: GetOp, 430 msg: &p2p.Message{ 431 Message: &p2p.Message_Get{ 432 Get: &p2p.Get{ 433 ChainId: testID[:], 434 RequestId: 1, 435 Deadline: 1, 436 ContainerId: testID[:], 437 }, 438 }, 439 }, 440 compressionType: compression.TypeNone, 441 bypassThrottling: true, 442 bytesSaved: false, 443 }, 444 { 445 desc: "put message with no compression", 446 op: PutOp, 447 msg: &p2p.Message{ 448 Message: &p2p.Message_Put{ 449 Put: &p2p.Put{ 450 ChainId: testID[:], 451 RequestId: 1, 452 Container: []byte{0}, 453 }, 454 }, 455 }, 456 compressionType: compression.TypeNone, 457 bypassThrottling: true, 458 bytesSaved: false, 459 }, 460 { 461 desc: "put message with zstd compression", 462 op: PutOp, 463 msg: &p2p.Message{ 464 Message: &p2p.Message_Put{ 465 Put: &p2p.Put{ 466 ChainId: testID[:], 467 RequestId: 1, 468 Container: compressibleContainers[0], 469 }, 470 }, 471 }, 472 compressionType: compression.TypeZstd, 473 bypassThrottling: true, 474 bytesSaved: true, 475 }, 476 { 477 desc: "push_query message with no compression", 478 op: PushQueryOp, 479 msg: &p2p.Message{ 480 Message: &p2p.Message_PushQuery{ 481 PushQuery: &p2p.PushQuery{ 482 ChainId: testID[:], 483 RequestId: 1, 484 Deadline: 1, 485 Container: []byte{0}, 486 }, 487 }, 488 }, 489 compressionType: compression.TypeNone, 490 bypassThrottling: true, 491 bytesSaved: false, 492 }, 493 { 494 desc: "push_query message with zstd compression", 495 op: PushQueryOp, 496 msg: &p2p.Message{ 497 Message: &p2p.Message_PushQuery{ 498 PushQuery: &p2p.PushQuery{ 499 ChainId: testID[:], 500 RequestId: 1, 501 Deadline: 1, 502 Container: compressibleContainers[0], 503 }, 504 }, 505 }, 506 compressionType: compression.TypeZstd, 507 bypassThrottling: true, 508 bytesSaved: true, 509 }, 510 { 511 desc: "pull_query message with no compression", 512 op: PullQueryOp, 513 msg: &p2p.Message{ 514 Message: &p2p.Message_PullQuery{ 515 PullQuery: &p2p.PullQuery{ 516 ChainId: testID[:], 517 RequestId: 1, 518 Deadline: 1, 519 ContainerId: testID[:], 520 }, 521 }, 522 }, 523 compressionType: compression.TypeNone, 524 bypassThrottling: true, 525 bytesSaved: false, 526 }, 527 { 528 desc: "chits message with no compression", 529 op: ChitsOp, 530 msg: &p2p.Message{ 531 Message: &p2p.Message_Chits{ 532 Chits: &p2p.Chits{ 533 ChainId: testID[:], 534 RequestId: 1, 535 PreferredId: testID[:], 536 }, 537 }, 538 }, 539 compressionType: compression.TypeNone, 540 bypassThrottling: true, 541 bytesSaved: false, 542 }, 543 { 544 desc: "app_request message with no compression", 545 op: AppRequestOp, 546 msg: &p2p.Message{ 547 Message: &p2p.Message_AppRequest{ 548 AppRequest: &p2p.AppRequest{ 549 ChainId: testID[:], 550 RequestId: 1, 551 Deadline: 1, 552 AppBytes: compressibleContainers[0], 553 }, 554 }, 555 }, 556 compressionType: compression.TypeNone, 557 bypassThrottling: true, 558 bytesSaved: false, 559 }, 560 { 561 desc: "app_request message with zstd compression", 562 op: AppRequestOp, 563 msg: &p2p.Message{ 564 Message: &p2p.Message_AppRequest{ 565 AppRequest: &p2p.AppRequest{ 566 ChainId: testID[:], 567 RequestId: 1, 568 Deadline: 1, 569 AppBytes: compressibleContainers[0], 570 }, 571 }, 572 }, 573 compressionType: compression.TypeZstd, 574 bypassThrottling: true, 575 bytesSaved: true, 576 }, 577 { 578 desc: "app_response message with no compression", 579 op: AppResponseOp, 580 msg: &p2p.Message{ 581 Message: &p2p.Message_AppResponse{ 582 AppResponse: &p2p.AppResponse{ 583 ChainId: testID[:], 584 RequestId: 1, 585 AppBytes: compressibleContainers[0], 586 }, 587 }, 588 }, 589 compressionType: compression.TypeNone, 590 bypassThrottling: true, 591 bytesSaved: false, 592 }, 593 { 594 desc: "app_response message with zstd compression", 595 op: AppResponseOp, 596 msg: &p2p.Message{ 597 Message: &p2p.Message_AppResponse{ 598 AppResponse: &p2p.AppResponse{ 599 ChainId: testID[:], 600 RequestId: 1, 601 AppBytes: compressibleContainers[0], 602 }, 603 }, 604 }, 605 compressionType: compression.TypeZstd, 606 bypassThrottling: true, 607 bytesSaved: true, 608 }, 609 { 610 desc: "app_gossip message with no compression", 611 op: AppGossipOp, 612 msg: &p2p.Message{ 613 Message: &p2p.Message_AppGossip{ 614 AppGossip: &p2p.AppGossip{ 615 ChainId: testID[:], 616 AppBytes: compressibleContainers[0], 617 }, 618 }, 619 }, 620 compressionType: compression.TypeNone, 621 bypassThrottling: true, 622 bytesSaved: false, 623 }, 624 { 625 desc: "app_gossip message with zstd compression", 626 op: AppGossipOp, 627 msg: &p2p.Message{ 628 Message: &p2p.Message_AppGossip{ 629 AppGossip: &p2p.AppGossip{ 630 ChainId: testID[:], 631 AppBytes: compressibleContainers[0], 632 }, 633 }, 634 }, 635 compressionType: compression.TypeZstd, 636 bypassThrottling: true, 637 bytesSaved: true, 638 }, 639 } 640 641 for _, tv := range tests { 642 t.Run(tv.desc, func(t *testing.T) { 643 require := require.New(t) 644 645 encodedMsg, err := mb.createOutbound(tv.msg, tv.compressionType, tv.bypassThrottling) 646 require.NoError(err) 647 648 require.Equal(tv.bypassThrottling, encodedMsg.BypassThrottling()) 649 require.Equal(tv.op, encodedMsg.Op()) 650 651 if bytesSaved := encodedMsg.BytesSavedCompression(); tv.bytesSaved { 652 require.Greater(bytesSaved, 0) 653 } 654 655 parsedMsg, err := mb.parseInbound(encodedMsg.Bytes(), ids.EmptyNodeID, func() {}) 656 require.NoError(err) 657 require.Equal(tv.op, parsedMsg.Op()) 658 }) 659 } 660 } 661 662 // Tests the Stringer interface on inbound messages 663 func TestInboundMessageToString(t *testing.T) { 664 t.Parallel() 665 666 require := require.New(t) 667 668 mb, err := newMsgBuilder( 669 logging.NoLog{}, 670 prometheus.NewRegistry(), 671 5*time.Second, 672 ) 673 require.NoError(err) 674 675 // msg that will become the tested InboundMessage 676 msg := &p2p.Message{ 677 Message: &p2p.Message_Pong{ 678 Pong: &p2p.Pong{}, 679 }, 680 } 681 msgBytes, err := proto.Marshal(msg) 682 require.NoError(err) 683 684 inboundMsg, err := mb.parseInbound(msgBytes, ids.EmptyNodeID, func() {}) 685 require.NoError(err) 686 687 require.Equal("NodeID-111111111111111111116DBWJs Op: pong Message: ", inboundMsg.String()) 688 689 internalMsg := InternalGetStateSummaryFrontierFailed(ids.EmptyNodeID, ids.Empty, 1) 690 require.Equal("NodeID-111111111111111111116DBWJs Op: get_state_summary_frontier_failed Message: ChainID: 11111111111111111111111111111111LpoYY RequestID: 1", internalMsg.String()) 691 } 692 693 func TestEmptyInboundMessage(t *testing.T) { 694 t.Parallel() 695 696 require := require.New(t) 697 698 mb, err := newMsgBuilder( 699 logging.NoLog{}, 700 prometheus.NewRegistry(), 701 5*time.Second, 702 ) 703 require.NoError(err) 704 705 msg := &p2p.Message{} 706 msgBytes, err := proto.Marshal(msg) 707 require.NoError(err) 708 709 _, err = mb.parseInbound(msgBytes, ids.EmptyNodeID, func() {}) 710 require.ErrorIs(err, errUnknownMessageType) 711 } 712 713 func TestNilInboundMessage(t *testing.T) { 714 t.Parallel() 715 716 require := require.New(t) 717 718 mb, err := newMsgBuilder( 719 logging.NoLog{}, 720 prometheus.NewRegistry(), 721 5*time.Second, 722 ) 723 require.NoError(err) 724 725 msg := &p2p.Message{ 726 Message: &p2p.Message_Ping{ 727 Ping: nil, 728 }, 729 } 730 msgBytes, err := proto.Marshal(msg) 731 require.NoError(err) 732 733 parsedMsg, err := mb.parseInbound(msgBytes, ids.EmptyNodeID, func() {}) 734 require.NoError(err) 735 736 require.IsType(&p2p.Ping{}, parsedMsg.message) 737 pingMsg := parsedMsg.message.(*p2p.Ping) 738 require.NotNil(pingMsg) 739 }