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