github.com/onflow/flow-go@v0.33.17/network/p2p/test/topic_validator_test.go (about) 1 package p2ptest_test 2 3 import ( 4 "context" 5 "fmt" 6 "sync" 7 "testing" 8 "time" 9 10 "github.com/libp2p/go-libp2p/core/peer" 11 "github.com/stretchr/testify/require" 12 13 "github.com/stretchr/testify/mock" 14 15 "github.com/onflow/flow-go/model/flow" 16 "github.com/onflow/flow-go/model/messages" 17 "github.com/onflow/flow-go/module/irrecoverable" 18 "github.com/onflow/flow-go/module/metrics" 19 mockmodule "github.com/onflow/flow-go/module/mock" 20 "github.com/onflow/flow-go/network" 21 "github.com/onflow/flow-go/network/alsp" 22 "github.com/onflow/flow-go/network/channels" 23 "github.com/onflow/flow-go/network/internal/p2pfixtures" 24 "github.com/onflow/flow-go/network/message" 25 "github.com/onflow/flow-go/network/mocknetwork" 26 "github.com/onflow/flow-go/network/p2p" 27 p2plogging "github.com/onflow/flow-go/network/p2p/logging" 28 p2ptest "github.com/onflow/flow-go/network/p2p/test" 29 "github.com/onflow/flow-go/network/p2p/translator" 30 "github.com/onflow/flow-go/network/p2p/utils" 31 "github.com/onflow/flow-go/network/slashing" 32 "github.com/onflow/flow-go/network/validator" 33 flowpubsub "github.com/onflow/flow-go/network/validator/pubsub" 34 "github.com/onflow/flow-go/utils/unittest" 35 ) 36 37 // TestTopicValidator_Unstaked tests that the libP2P node topic validator rejects unauthenticated messages on non-public channels (unstaked) 38 func TestTopicValidator_Unstaked(t *testing.T) { 39 ctx, cancel := context.WithCancel(context.Background()) 40 signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) 41 idProvider := mockmodule.NewIdentityProvider(t) 42 // create a hooked logger 43 logger, hook := unittest.HookedLogger() 44 45 sporkId := unittest.IdentifierFixture() 46 47 sn1, identity1 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) 48 sn2, identity2 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) 49 idProvider.On("ByPeerID", sn1.ID()).Return(&identity1, true).Maybe() 50 idProvider.On("ByPeerID", sn2.ID()).Return(&identity2, true).Maybe() 51 nodes := []p2p.LibP2PNode{sn1, sn2} 52 p2ptest.StartNodes(t, signalerCtx, nodes) 53 defer p2ptest.StopNodes(t, nodes, cancel) 54 55 channel := channels.ConsensusCommittee 56 topic := channels.TopicFromChannel(channel, sporkId) 57 58 // NOTE: identity2 is not in the ids list simulating an un-staked node 59 ids := flow.IdentityList{&identity1} 60 translatorFixture, err := translator.NewFixedTableIdentityTranslator(ids) 61 require.NoError(t, err) 62 63 // peer filter used by the topic validator to check if node is staked 64 isStaked := func(pid peer.ID) error { 65 fid, err := translatorFixture.GetFlowID(pid) 66 if err != nil { 67 return fmt.Errorf("could not translate the peer_id %s to a Flow identifier: %w", p2plogging.PeerId(pid), err) 68 } 69 70 if _, ok := ids.ByNodeID(fid); !ok { 71 return fmt.Errorf("flow id not found: %x", fid) 72 } 73 74 return nil 75 } 76 77 pInfo2, err := utils.PeerAddressInfo(identity2) 78 require.NoError(t, err) 79 80 // node1 is connected to node2 81 // sn1 <-> sn2 82 require.NoError(t, sn1.ConnectToPeer(ctx, pInfo2)) 83 84 // sn1 will subscribe with is staked callback that should force the TopicValidator to drop the message received from sn2 85 sub1, err := sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, isStaked)) 86 require.NoError(t, err) 87 88 // sn2 will subscribe with an unauthenticated callback to allow it to send the unauthenticated message 89 _, err = sn2.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter())) 90 require.NoError(t, err) 91 92 // let nodes form the mesh 93 time.Sleep(time.Second) 94 95 timedCtx, cancel5s := context.WithTimeout(ctx, 5*time.Second) 96 defer cancel5s() 97 98 outgoingMessageScope1, err := message.NewOutgoingScope( 99 flow.IdentifierList{identity1.NodeID, identity2.NodeID}, 100 topic, 101 unittest.ProposalFixture(), 102 unittest.NetworkCodec().Encode, 103 message.ProtocolTypePubSub) 104 require.NoError(t, err) 105 106 err = sn2.Publish(timedCtx, outgoingMessageScope1) 107 require.NoError(t, err) 108 109 // sn1 should not receive message from sn2 because sn2 is unstaked 110 timedCtx, cancel1s := context.WithTimeout(ctx, time.Second) 111 defer cancel1s() 112 p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, sub1) 113 114 // ensure the correct error is contained in the logged error 115 require.Contains(t, hook.Logs(), "filtering message from un-allowed peer") 116 } 117 118 // TestTopicValidator_PublicChannel tests that the libP2P node topic validator does not reject unauthenticated messages on public channels 119 func TestTopicValidator_PublicChannel(t *testing.T) { 120 ctx, cancel := context.WithCancel(context.Background()) 121 signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) 122 idProvider := mockmodule.NewIdentityProvider(t) 123 sporkId := unittest.IdentifierFixture() 124 logger := unittest.Logger() 125 126 sn1, identity1 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) 127 sn2, identity2 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) 128 idProvider.On("ByPeerID", sn1.ID()).Return(&identity1, true).Maybe() 129 idProvider.On("ByPeerID", sn2.ID()).Return(&identity2, true).Maybe() 130 nodes := []p2p.LibP2PNode{sn1, sn2} 131 p2ptest.StartNodes(t, signalerCtx, nodes) 132 defer p2ptest.StopNodes(t, nodes, cancel) 133 134 // unauthenticated messages should not be dropped on public channels 135 channel := channels.PublicSyncCommittee 136 topic := channels.TopicFromChannel(channel, sporkId) 137 138 pInfo2, err := utils.PeerAddressInfo(identity2) 139 require.NoError(t, err) 140 141 // node1 is connected to node2 142 // sn1 <-> sn2 143 require.NoError(t, sn1.ConnectToPeer(ctx, pInfo2)) 144 145 // sn1 & sn2 will subscribe with unauthenticated callback to allow it to send and receive unauthenticated messages 146 sub1, err := sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter())) 147 require.NoError(t, err) 148 sub2, err := sn2.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter())) 149 require.NoError(t, err) 150 151 // let nodes form the mesh 152 time.Sleep(time.Second) 153 154 timedCtx, cancel5s := context.WithTimeout(ctx, 5*time.Second) 155 defer cancel5s() 156 157 outgoingMessageScope1, err := message.NewOutgoingScope( 158 flow.IdentifierList{identity1.NodeID, identity2.NodeID}, 159 topic, 160 &messages.SyncRequest{Nonce: 0, Height: 0}, 161 unittest.NetworkCodec().Encode, 162 message.ProtocolTypePubSub) 163 require.NoError(t, err) 164 165 err = sn2.Publish(timedCtx, outgoingMessageScope1) 166 require.NoError(t, err) 167 168 var wg sync.WaitGroup 169 170 // sn1 should receive message from sn2 because the public channel is unauthenticated 171 timedCtx, cancel1s := context.WithTimeout(ctx, time.Second) 172 defer cancel1s() 173 174 expectedReceivedData, err := outgoingMessageScope1.Proto().Marshal() 175 require.NoError(t, err) 176 177 // sn1 gets the message 178 p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData, sub1) 179 180 // sn2 also gets the message (as part of the libp2p loopback of published topic messages) 181 p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData, sub2) 182 183 unittest.RequireReturnsBefore(t, wg.Wait, 5*time.Second, "could not receive message on time") 184 } 185 186 // TestTopicValidator_TopicMismatch tests that the libP2P node topic validator rejects messages with mismatched topics 187 func TestTopicValidator_TopicMismatch(t *testing.T) { 188 ctx, cancel := context.WithCancel(context.Background()) 189 signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) 190 idProvider := mockmodule.NewIdentityProvider(t) 191 // create a hooked logger 192 logger, hook := unittest.HookedLogger() 193 194 sporkId := unittest.IdentifierFixture() 195 196 sn1, identity1 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) 197 sn2, identity2 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) 198 idProvider.On("ByPeerID", sn1.ID()).Return(&identity1, true).Maybe() 199 idProvider.On("ByPeerID", sn2.ID()).Return(&identity2, true).Maybe() 200 nodes := []p2p.LibP2PNode{sn1, sn2} 201 p2ptest.StartNodes(t, signalerCtx, nodes) 202 defer p2ptest.StopNodes(t, nodes, cancel) 203 204 channel := channels.ConsensusCommittee 205 topic := channels.TopicFromChannel(channel, sporkId) 206 207 pInfo2, err := utils.PeerAddressInfo(identity2) 208 require.NoError(t, err) 209 210 // node1 is connected to node2 211 // sn1 <-> sn2 212 require.NoError(t, sn1.ConnectToPeer(ctx, pInfo2)) 213 214 // sn2 will subscribe with an unauthenticated callback to allow processing of message after the authorization check 215 _, err = sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter())) 216 require.NoError(t, err) 217 218 // sn2 will subscribe with an unauthenticated callback to allow it to send the unauthenticated message 219 _, err = sn2.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter())) 220 require.NoError(t, err) 221 222 // let nodes form the mesh 223 time.Sleep(time.Second) 224 225 timedCtx, cancel5s := context.WithTimeout(ctx, 5*time.Second) 226 defer cancel5s() 227 228 // create a dummy block proposal to publish from our SN node 229 outgoingMessageScope1, err := message.NewOutgoingScope( 230 flow.IdentifierList{identity1.NodeID, identity2.NodeID}, 231 topic, 232 unittest.ProposalFixture(), 233 unittest.NetworkCodec().Encode, 234 message.ProtocolTypePubSub) 235 require.NoError(t, err) 236 237 // intentionally overriding the channel id to be different from the topic 238 outgoingMessageScope1.Proto().ChannelID = channels.PublicSyncCommittee.String() 239 240 err = sn2.Publish(timedCtx, outgoingMessageScope1) 241 // publish fails because the channel validation fails 242 require.Error(t, err) 243 244 // ensure the correct error is contained in the logged error 245 require.Contains(t, hook.Logs(), "channel id in message does not match pubsub topic") 246 } 247 248 // TestTopicValidator_InvalidTopic tests that the libP2P node topic validator rejects messages with invalid topics 249 func TestTopicValidator_InvalidTopic(t *testing.T) { 250 ctx, cancel := context.WithCancel(context.Background()) 251 signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) 252 idProvider := mockmodule.NewIdentityProvider(t) 253 // create a hooked logger 254 logger, hook := unittest.HookedLogger() 255 256 sporkId := unittest.IdentifierFixture() 257 258 sn1, identity1 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) 259 sn2, identity2 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus), p2ptest.WithLogger(logger)) 260 idProvider.On("ByPeerID", sn1.ID()).Return(&identity1, true).Maybe() 261 idProvider.On("ByPeerID", sn2.ID()).Return(&identity2, true).Maybe() 262 nodes := []p2p.LibP2PNode{sn1, sn2} 263 p2ptest.StartNodes(t, signalerCtx, nodes) 264 defer p2ptest.StopNodes(t, nodes, cancel) 265 266 topic := channels.Topic("invalid-topic") 267 268 pInfo2, err := utils.PeerAddressInfo(identity2) 269 require.NoError(t, err) 270 271 // node1 is connected to node2 272 // sn1 <-> sn2 273 require.NoError(t, sn1.ConnectToPeer(ctx, pInfo2)) 274 275 // sn2 will subscribe with an unauthenticated callback to allow processing of message after the authorization check 276 _, err = sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter())) 277 require.NoError(t, err) 278 279 // sn2 will subscribe with an unauthenticated callback to allow it to send the unauthenticated message 280 _, err = sn2.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter())) 281 require.NoError(t, err) 282 283 // let nodes form the mesh 284 time.Sleep(time.Second) 285 286 timedCtx, cancel5s := context.WithTimeout(ctx, 5*time.Second) 287 defer cancel5s() 288 289 // invalid topic is malformed, hence it cannot be used to create a message scope, as it faces an error. 290 // Hence, we create a dummy block proposal message scope to publish on a legit topic, and then override 291 // the topic in the next step to a malformed topic. 292 dummyMessageScope, err := message.NewOutgoingScope( 293 flow.IdentifierList{identity1.NodeID, identity2.NodeID}, 294 channels.TopicFromChannel(channels.PushBlocks, sporkId), 295 unittest.ProposalFixture(), 296 unittest.NetworkCodec().Encode, 297 message.ProtocolTypePubSub) 298 require.NoError(t, err) 299 300 // overrides the topic to be an invalid topic 301 corruptOutgoingMessageScope := mocknetwork.NewOutgoingMessageScope(t) 302 corruptOutgoingMessageScope.On("Topic").Return(topic) 303 corruptOutgoingMessageScope.On("Proto").Return(dummyMessageScope.Proto()) 304 corruptOutgoingMessageScope.On("PayloadType").Return(dummyMessageScope.PayloadType()) 305 corruptOutgoingMessageScope.On("Size").Return(dummyMessageScope.Size()) 306 307 // create a dummy block proposal to publish from our SN node 308 err = sn2.Publish(timedCtx, corruptOutgoingMessageScope) 309 310 // publish fails because the topic conversion fails 311 require.Error(t, err) 312 // ensure the correct error is contained in the logged error 313 require.Contains(t, hook.Logs(), "could not convert topic to channel") 314 } 315 316 // TestAuthorizedSenderValidator_Unauthorized tests that the authorized sender validator rejects messages from nodes that are not authorized to send the message 317 func TestAuthorizedSenderValidator_Unauthorized(t *testing.T) { 318 ctx, cancel := context.WithCancel(context.Background()) 319 signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) 320 idProvider := mockmodule.NewIdentityProvider(t) 321 logger := unittest.Logger() 322 323 sporkId := unittest.IdentifierFixture() 324 325 sn1, identity1 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus)) 326 sn2, identity2 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleConsensus)) 327 an1, identity3 := p2ptest.NodeFixture(t, sporkId, t.Name(), idProvider, p2ptest.WithRole(flow.RoleAccess)) 328 idProvider.On("ByPeerID", sn1.ID()).Return(&identity1, true).Maybe() 329 idProvider.On("ByPeerID", sn2.ID()).Return(&identity2, true).Maybe() 330 idProvider.On("ByPeerID", an1.ID()).Return(&identity3, true).Maybe() 331 nodes := []p2p.LibP2PNode{sn1, sn2, an1} 332 p2ptest.StartNodes(t, signalerCtx, nodes) 333 defer p2ptest.StopNodes(t, nodes, cancel) 334 335 channel := channels.ConsensusCommittee 336 topic := channels.TopicFromChannel(channel, sporkId) 337 338 ids := flow.IdentityList{&identity1, &identity2, &identity3} 339 340 translatorFixture, err := translator.NewFixedTableIdentityTranslator(ids) 341 require.NoError(t, err) 342 343 violation := &network.Violation{ 344 Identity: &identity3, 345 PeerID: p2plogging.PeerId(an1.ID()), 346 OriginID: identity3.NodeID, 347 MsgType: "*messages.BlockProposal", 348 Channel: channel, 349 Protocol: message.ProtocolTypePubSub, 350 Err: message.ErrUnauthorizedRole, 351 } 352 violationsConsumer := mocknetwork.NewViolationsConsumer(t) 353 violationsConsumer.On("OnUnAuthorizedSenderError", violation).Once().Return(nil) 354 getIdentity := func(pid peer.ID) (*flow.Identity, bool) { 355 fid, err := translatorFixture.GetFlowID(pid) 356 if err != nil { 357 return &flow.Identity{}, false 358 } 359 360 return ids.ByNodeID(fid) 361 } 362 authorizedSenderValidator := validator.NewAuthorizedSenderValidator(logger, violationsConsumer, getIdentity) 363 pubsubMessageValidator := authorizedSenderValidator.PubSubMessageValidator(channel) 364 365 pInfo1, err := utils.PeerAddressInfo(identity1) 366 require.NoError(t, err) 367 368 pInfo2, err := utils.PeerAddressInfo(identity2) 369 require.NoError(t, err) 370 371 // node1 is connected to node2, and the an1 is connected to node1 372 // an1 <-> sn1 <-> sn2 373 require.NoError(t, sn1.ConnectToPeer(ctx, pInfo2)) 374 require.NoError(t, an1.ConnectToPeer(ctx, pInfo1)) 375 376 // sn1 and sn2 subscribe to the topic with the topic validator 377 sub1, err := sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter(), pubsubMessageValidator)) 378 require.NoError(t, err) 379 sub2, err := sn2.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter(), pubsubMessageValidator)) 380 require.NoError(t, err) 381 sub3, err := an1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter())) 382 require.NoError(t, err) 383 384 // let nodes form the mesh 385 time.Sleep(time.Second) 386 387 timedCtx, cancel5s := context.WithTimeout(ctx, 60*time.Second) 388 defer cancel5s() 389 390 // sn2 publishes the block proposal, sn1 and an1 should receive the message because 391 // SN nodes are authorized to send block proposals 392 // create a dummy block proposal to publish from our SN node 393 outgoingMessageScope1, err := message.NewOutgoingScope( 394 flow.IdentifierList{identity1.NodeID, identity2.NodeID}, 395 topic, 396 unittest.ProposalFixture(), 397 unittest.NetworkCodec().Encode, 398 message.ProtocolTypePubSub) 399 require.NoError(t, err) 400 err = sn2.Publish(timedCtx, outgoingMessageScope1) 401 require.NoError(t, err) 402 403 expectedReceivedData1, err := outgoingMessageScope1.Proto().Marshal() 404 require.NoError(t, err) 405 406 // sn1 gets the message 407 p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData1, sub1) 408 409 // sn2 also gets the message (as part of the libp2p loopback of published topic messages) 410 p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData1, sub2) 411 412 // an1 also gets the message 413 p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData1, sub3) 414 415 timedCtx, cancel2s := context.WithTimeout(ctx, 2*time.Second) 416 defer cancel2s() 417 418 // the access node now publishes the block proposal message, AN are not authorized to publish block proposals 419 // the message should be rejected by the topic validator on sn1 420 outgoingMessageScope2, err := message.NewOutgoingScope( 421 flow.IdentifierList{identity1.NodeID, identity2.NodeID}, 422 topic, 423 unittest.ProposalFixture(), 424 unittest.NetworkCodec().Encode, 425 message.ProtocolTypePubSub) 426 require.NoError(t, err) 427 err = an1.Publish(timedCtx, outgoingMessageScope2) 428 require.NoError(t, err) 429 430 expectedReceivedData2, err := outgoingMessageScope2.Proto().Marshal() 431 require.NoError(t, err) 432 433 // an1 receives its own message 434 p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData2, sub3) 435 436 var wg sync.WaitGroup 437 438 // sn1 does NOT receive the message due to the topic validator 439 timedCtx, cancel1s := context.WithTimeout(ctx, time.Second) 440 defer cancel1s() 441 p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, sub1) 442 443 // sn2 also does not receive the message via gossip from the sn1 (event after the 1 second hearbeat) 444 timedCtx, cancel2s = context.WithTimeout(ctx, 2*time.Second) 445 defer cancel2s() 446 p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, sub2) 447 448 unittest.RequireReturnsBefore(t, wg.Wait, 5*time.Second, "could not receive message on time") 449 } 450 451 // TestAuthorizedSenderValidator_Authorized tests that the authorized sender validator rejects messages being sent on the wrong channel 452 func TestAuthorizedSenderValidator_InvalidMsg(t *testing.T) { 453 ctx, cancel := context.WithCancel(context.Background()) 454 signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) 455 idProvider := mockmodule.NewIdentityProvider(t) 456 // create a hooked logger 457 logger, hook := unittest.HookedLogger() 458 459 sporkId := unittest.IdentifierFixture() 460 461 sn1, identity1 := p2ptest.NodeFixture(t, sporkId, "consensus_1", idProvider, p2ptest.WithRole(flow.RoleConsensus)) 462 sn2, identity2 := p2ptest.NodeFixture(t, sporkId, "consensus_2", idProvider, p2ptest.WithRole(flow.RoleConsensus)) 463 idProvider.On("ByPeerID", sn1.ID()).Return(&identity1, true).Maybe() 464 idProvider.On("ByPeerID", sn2.ID()).Return(&identity2, true).Maybe() 465 nodes := []p2p.LibP2PNode{sn1, sn2} 466 p2ptest.StartNodes(t, signalerCtx, nodes) 467 defer p2ptest.StopNodes(t, nodes, cancel) 468 469 // try to publish BlockProposal on invalid SyncCommittee channel 470 channel := channels.SyncCommittee 471 topic := channels.TopicFromChannel(channel, sporkId) 472 473 ids := flow.IdentityList{&identity1, &identity2} 474 translatorFixture, err := translator.NewFixedTableIdentityTranslator(ids) 475 require.NoError(t, err) 476 477 expectedMisbehaviorReport, err := alsp.NewMisbehaviorReport(identity2.NodeID, alsp.UnAuthorizedSender) 478 require.NoError(t, err) 479 misbehaviorReportConsumer := mocknetwork.NewMisbehaviorReportConsumer(t) 480 misbehaviorReportConsumer.On("ReportMisbehaviorOnChannel", channel, expectedMisbehaviorReport).Once() 481 violationsConsumer := slashing.NewSlashingViolationsConsumer(logger, metrics.NewNoopCollector(), misbehaviorReportConsumer) 482 getIdentity := func(pid peer.ID) (*flow.Identity, bool) { 483 fid, err := translatorFixture.GetFlowID(pid) 484 if err != nil { 485 return &flow.Identity{}, false 486 } 487 488 return ids.ByNodeID(fid) 489 } 490 authorizedSenderValidator := validator.NewAuthorizedSenderValidator(logger, violationsConsumer, getIdentity) 491 pubsubMessageValidator := authorizedSenderValidator.PubSubMessageValidator(channel) 492 493 pInfo2, err := utils.PeerAddressInfo(identity2) 494 require.NoError(t, err) 495 496 // node1 is connected to node2 497 // sn1 <-> sn2 498 require.NoError(t, sn1.ConnectToPeer(ctx, pInfo2)) 499 500 // sn1 subscribe to the topic with the topic validator, while sn2 will subscribe without the topic validator to allow sn2 to publish unauthorized messages 501 sub1, err := sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter(), pubsubMessageValidator)) 502 require.NoError(t, err) 503 _, err = sn2.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter())) 504 require.NoError(t, err) 505 506 // let nodes form the mesh 507 time.Sleep(time.Second) 508 509 timedCtx, cancel5s := context.WithTimeout(ctx, 5*time.Second) 510 defer cancel5s() 511 512 // create a dummy block proposal to publish from our SN node 513 // sn2 publishes the block proposal on the sync committee channel 514 outgoingMessageScope1, err := message.NewOutgoingScope( 515 flow.IdentifierList{identity1.NodeID, identity2.NodeID}, 516 topic, 517 unittest.ProposalFixture(), 518 unittest.NetworkCodec().Encode, 519 message.ProtocolTypePubSub) 520 require.NoError(t, err) 521 err = sn2.Publish(timedCtx, outgoingMessageScope1) 522 require.NoError(t, err) 523 524 // sn1 should not receive message from sn2 525 timedCtx, cancel1s := context.WithTimeout(ctx, time.Second) 526 defer cancel1s() 527 p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, sub1) 528 529 // ensure the correct error is contained in the logged error 530 require.Contains(t, hook.Logs(), message.ErrUnauthorizedMessageOnChannel.Error()) 531 } 532 533 // TestAuthorizedSenderValidator_Ejected tests that the authorized sender validator rejects messages from nodes that are ejected 534 func TestAuthorizedSenderValidator_Ejected(t *testing.T) { 535 ctx, cancel := context.WithCancel(context.Background()) 536 signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) 537 idProvider := mockmodule.NewIdentityProvider(t) 538 // create a hooked logger 539 logger, hook := unittest.HookedLogger() 540 541 sporkId := unittest.IdentifierFixture() 542 543 sn1, identity1 := p2ptest.NodeFixture(t, sporkId, "consensus_1", idProvider, p2ptest.WithRole(flow.RoleConsensus)) 544 sn2, identity2 := p2ptest.NodeFixture(t, sporkId, "consensus_2", idProvider, p2ptest.WithRole(flow.RoleConsensus)) 545 an1, identity3 := p2ptest.NodeFixture(t, sporkId, "access_1", idProvider, p2ptest.WithRole(flow.RoleAccess)) 546 idProvider.On("ByPeerID", sn1.ID()).Return(&identity1, true).Maybe() 547 idProvider.On("ByPeerID", sn2.ID()).Return(&identity2, true).Maybe() 548 idProvider.On("ByPeerID", an1.ID()).Return(&identity3, true).Maybe() 549 nodes := []p2p.LibP2PNode{sn1, sn2, an1} 550 p2ptest.StartNodes(t, signalerCtx, nodes) 551 defer p2ptest.StopNodes(t, nodes, cancel) 552 553 channel := channels.ConsensusCommittee 554 topic := channels.TopicFromChannel(channel, sporkId) 555 556 ids := flow.IdentityList{&identity1, &identity2, &identity3} 557 translatorFixture, err := translator.NewFixedTableIdentityTranslator(ids) 558 require.NoError(t, err) 559 560 expectedMisbehaviorReport, err := alsp.NewMisbehaviorReport(identity2.NodeID, alsp.SenderEjected) 561 require.NoError(t, err) 562 misbehaviorReportConsumer := mocknetwork.NewMisbehaviorReportConsumer(t) 563 misbehaviorReportConsumer.On("ReportMisbehaviorOnChannel", channel, expectedMisbehaviorReport).Once() 564 violationsConsumer := slashing.NewSlashingViolationsConsumer(logger, metrics.NewNoopCollector(), misbehaviorReportConsumer) 565 getIdentity := func(pid peer.ID) (*flow.Identity, bool) { 566 fid, err := translatorFixture.GetFlowID(pid) 567 if err != nil { 568 return &flow.Identity{}, false 569 } 570 571 return ids.ByNodeID(fid) 572 } 573 authorizedSenderValidator := validator.NewAuthorizedSenderValidator(logger, violationsConsumer, getIdentity) 574 pubsubMessageValidator := authorizedSenderValidator.PubSubMessageValidator(channel) 575 576 pInfo1, err := utils.PeerAddressInfo(identity1) 577 require.NoError(t, err) 578 579 pInfo2, err := utils.PeerAddressInfo(identity2) 580 require.NoError(t, err) 581 582 // node1 is connected to node2, and the an1 is connected to node1 583 // an1 <-> sn1 <-> sn2 584 require.NoError(t, sn1.ConnectToPeer(ctx, pInfo2)) 585 require.NoError(t, an1.ConnectToPeer(ctx, pInfo1)) 586 587 // sn1 subscribe to the topic with the topic validator, while sn2 will subscribe without the topic validator to allow sn2 to publish unauthorized messages 588 sub1, err := sn1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter(), pubsubMessageValidator)) 589 require.NoError(t, err) 590 sub2, err := sn2.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter())) 591 require.NoError(t, err) 592 sub3, err := an1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter())) 593 require.NoError(t, err) 594 595 // let nodes form the mesh 596 time.Sleep(time.Second) 597 598 timedCtx, cancel5s := context.WithTimeout(ctx, 5*time.Second) 599 defer cancel5s() 600 601 // sn2 publishes the block proposal, sn1 and an1 should receive the message because 602 // SN nodes are authorized to send block proposals 603 // create a dummy block proposal to publish from our SN node 604 outgoingMessageScope1, err := message.NewOutgoingScope( 605 flow.IdentifierList{identity1.NodeID, identity2.NodeID}, 606 topic, 607 unittest.ProposalFixture(), 608 unittest.NetworkCodec().Encode, 609 message.ProtocolTypePubSub) 610 require.NoError(t, err) 611 err = sn2.Publish(timedCtx, outgoingMessageScope1) 612 require.NoError(t, err) 613 614 expectedReceivedData1, err := outgoingMessageScope1.Proto().Marshal() 615 require.NoError(t, err) 616 617 // sn1 gets the message 618 p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData1, sub1) 619 620 // sn2 also gets the message (as part of the libp2p loopback of published topic messages) 621 p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData1, sub2) 622 623 // an1 also gets the message 624 p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData1, sub3) 625 626 // "eject" sn2 to ensure messages published by ejected nodes get rejected 627 identity2.Ejected = true 628 629 outgoingMessageScope3, err := message.NewOutgoingScope( 630 flow.IdentifierList{identity1.NodeID, identity2.NodeID}, 631 topic, 632 unittest.ProposalFixture(), 633 unittest.NetworkCodec().Encode, 634 message.ProtocolTypePubSub) 635 require.NoError(t, err) 636 637 timedCtx, cancel2s := context.WithTimeout(ctx, time.Second) 638 defer cancel2s() 639 err = sn2.Publish(timedCtx, outgoingMessageScope3) 640 require.NoError(t, err) 641 642 // sn1 should not receive rejected message from ejected sn2 643 timedCtx, cancel1s := context.WithTimeout(ctx, time.Second) 644 defer cancel1s() 645 p2pfixtures.SubMustNeverReceiveAnyMessage(t, timedCtx, sub1) 646 647 // ensure the correct error is contained in the logged error 648 require.Contains(t, hook.Logs(), validator.ErrSenderEjected.Error()) 649 } 650 651 // TestAuthorizedSenderValidator_ClusterChannel tests that the authorized sender validator correctly validates messages sent on cluster channels 652 func TestAuthorizedSenderValidator_ClusterChannel(t *testing.T) { 653 ctx, cancel := context.WithCancel(context.Background()) 654 signalerCtx := irrecoverable.NewMockSignalerContext(t, ctx) 655 idProvider := mockmodule.NewIdentityProvider(t) 656 sporkId := unittest.IdentifierFixture() 657 658 ln1, identity1 := p2ptest.NodeFixture(t, sporkId, "collection_1", idProvider, p2ptest.WithRole(flow.RoleCollection)) 659 ln2, identity2 := p2ptest.NodeFixture(t, sporkId, "collection_2", idProvider, p2ptest.WithRole(flow.RoleCollection)) 660 ln3, identity3 := p2ptest.NodeFixture(t, sporkId, "collection_3", idProvider, p2ptest.WithRole(flow.RoleCollection)) 661 idProvider.On("ByPeerID", ln1.ID()).Return(&identity1, true).Maybe() 662 idProvider.On("ByPeerID", ln2.ID()).Return(&identity2, true).Maybe() 663 idProvider.On("ByPeerID", ln3.ID()).Return(&identity3, true).Maybe() 664 nodes := []p2p.LibP2PNode{ln1, ln2, ln3} 665 p2ptest.StartNodes(t, signalerCtx, nodes) 666 defer p2ptest.StopNodes(t, nodes, cancel) 667 668 channel := channels.SyncCluster(flow.Testnet) 669 topic := channels.TopicFromChannel(channel, sporkId) 670 671 ids := flow.IdentityList{&identity1, &identity2, &identity3} 672 translatorFixture, err := translator.NewFixedTableIdentityTranslator(ids) 673 require.NoError(t, err) 674 675 logger := unittest.Logger() 676 misbehaviorReportConsumer := mocknetwork.NewMisbehaviorReportConsumer(t) 677 defer misbehaviorReportConsumer.AssertNotCalled(t, "ReportMisbehaviorOnChannel", mock.AnythingOfType("channels.Channel"), mock.AnythingOfType("*alsp.MisbehaviorReport")) 678 violationsConsumer := slashing.NewSlashingViolationsConsumer(logger, metrics.NewNoopCollector(), misbehaviorReportConsumer) 679 getIdentity := func(pid peer.ID) (*flow.Identity, bool) { 680 fid, err := translatorFixture.GetFlowID(pid) 681 if err != nil { 682 return &flow.Identity{}, false 683 } 684 685 return ids.ByNodeID(fid) 686 } 687 authorizedSenderValidator := validator.NewAuthorizedSenderValidator(logger, violationsConsumer, getIdentity) 688 pubsubMessageValidator := authorizedSenderValidator.PubSubMessageValidator(channel) 689 690 pInfo1, err := utils.PeerAddressInfo(identity1) 691 require.NoError(t, err) 692 693 pInfo2, err := utils.PeerAddressInfo(identity2) 694 require.NoError(t, err) 695 696 // ln3 <-> sn1 <-> sn2 697 require.NoError(t, ln1.ConnectToPeer(ctx, pInfo2)) 698 require.NoError(t, ln3.ConnectToPeer(ctx, pInfo1)) 699 700 sub1, err := ln1.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter(), pubsubMessageValidator)) 701 require.NoError(t, err) 702 sub2, err := ln2.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter(), pubsubMessageValidator)) 703 require.NoError(t, err) 704 sub3, err := ln3.Subscribe(topic, flowpubsub.TopicValidator(logger, unittest.AllowAllPeerFilter(), pubsubMessageValidator)) 705 require.NoError(t, err) 706 707 // let nodes form the mesh 708 time.Sleep(time.Second) 709 710 timedCtx, cancel5s := context.WithTimeout(ctx, 5*time.Second) 711 defer cancel5s() 712 713 // create a dummy sync request to publish from our LN node 714 outgoingMessageScope1, err := message.NewOutgoingScope( 715 flow.IdentifierList{identity1.NodeID, identity2.NodeID}, 716 topic, 717 &messages.RangeRequest{}, 718 unittest.NetworkCodec().Encode, 719 message.ProtocolTypePubSub) 720 require.NoError(t, err) 721 722 // ln2 publishes the sync request on the cluster channel 723 err = ln2.Publish(timedCtx, outgoingMessageScope1) 724 require.NoError(t, err) 725 726 expectedReceivedData1, err := outgoingMessageScope1.Proto().Marshal() 727 require.NoError(t, err) 728 729 // ln1 gets the message 730 p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData1, sub1) 731 732 // ln2 also gets the message (as part of the libp2p loopback of published topic messages) 733 p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData1, sub2) 734 735 // ln3 also gets the message 736 p2pfixtures.SubMustReceiveMessage(t, timedCtx, expectedReceivedData1, sub3) 737 }