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  }