github.com/koko1123/flow-go-1@v0.29.6/network/validator/authorized_sender_validator_test.go (about) 1 package validator 2 3 import ( 4 "fmt" 5 "testing" 6 7 "github.com/libp2p/go-libp2p/core/peer" 8 "github.com/rs/zerolog" 9 "github.com/stretchr/testify/require" 10 "github.com/stretchr/testify/suite" 11 12 "github.com/koko1123/flow-go-1/model/flow" 13 "github.com/koko1123/flow-go-1/model/messages" 14 "github.com/koko1123/flow-go-1/module/metrics" 15 "github.com/koko1123/flow-go-1/network/channels" 16 "github.com/koko1123/flow-go-1/network/message" 17 "github.com/koko1123/flow-go-1/network/p2p" 18 "github.com/koko1123/flow-go-1/network/slashing" 19 "github.com/koko1123/flow-go-1/utils/unittest" 20 ) 21 22 type TestCase struct { 23 Identity *flow.Identity 24 GetIdentity func(pid peer.ID) (*flow.Identity, bool) 25 Channel channels.Channel 26 Message interface{} 27 MessageStr string 28 Protocols message.Protocols 29 } 30 31 func TestIsAuthorizedSender(t *testing.T) { 32 suite.Run(t, new(TestAuthorizedSenderValidatorSuite)) 33 } 34 35 type TestAuthorizedSenderValidatorSuite struct { 36 suite.Suite 37 authorizedSenderTestCases []TestCase 38 unauthorizedSenderTestCases []TestCase 39 unauthorizedMessageOnChannelTestCases []TestCase 40 unauthorizedUnicastOnChannel []TestCase 41 authorizedUnicastOnChannel []TestCase 42 log zerolog.Logger 43 slashingViolationsConsumer slashing.ViolationsConsumer 44 allMsgConfigs []message.MsgAuthConfig 45 } 46 47 func (s *TestAuthorizedSenderValidatorSuite) SetupTest() { 48 s.allMsgConfigs = message.GetAllMessageAuthConfigs() 49 s.initializeAuthorizationTestCases() 50 s.initializeInvalidMessageOnChannelTestCases() 51 s.initializeUnicastOnChannelTestCases() 52 s.log = unittest.Logger() 53 s.slashingViolationsConsumer = slashing.NewSlashingViolationsConsumer(s.log, metrics.NewNoopCollector()) 54 } 55 56 // TestValidatorCallback_AuthorizedSender checks that AuthorizedSenderValidator.Validate does not return false positive 57 // validation errors for all possible valid combinations (authorized sender role, message type). 58 func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_AuthorizedSender() { 59 for _, c := range s.authorizedSenderTestCases { 60 str := fmt.Sprintf("role (%s) should be authorized to send message type (%s) on channel (%s)", c.Identity.Role, c.MessageStr, c.Channel) 61 s.Run(str, func() { 62 authorizedSenderValidator := NewAuthorizedSenderValidator(s.log, s.slashingViolationsConsumer, c.GetIdentity) 63 64 pid, err := unittest.PeerIDFromFlowID(c.Identity) 65 require.NoError(s.T(), err) 66 67 // ensure according to the message auth config, if a message is authorized to be sent via unicast it 68 // is accepted or rejected. 69 msgType, err := authorizedSenderValidator.Validate(pid, c.Message, c.Channel, message.ProtocolUnicast) 70 if c.Protocols.Contains(message.ProtocolUnicast) { 71 require.NoError(s.T(), err) 72 require.Equal(s.T(), c.MessageStr, msgType) 73 } else { 74 require.ErrorIs(s.T(), err, message.ErrUnauthorizedUnicastOnChannel) 75 require.Equal(s.T(), c.MessageStr, msgType) 76 } 77 78 validatePubsub := authorizedSenderValidator.PubSubMessageValidator(c.Channel) 79 pubsubResult := validatePubsub(pid, c.Message) 80 if !c.Protocols.Contains(message.ProtocolPublish) { 81 require.Equal(s.T(), p2p.ValidationReject, pubsubResult) 82 } else { 83 require.Equal(s.T(), p2p.ValidationAccept, pubsubResult) 84 } 85 }) 86 } 87 } 88 89 // TestValidatorCallback_UnAuthorizedSender checks that AuthorizedSenderValidator.Validate return's p2p.ValidationReject 90 // validation error for all possible invalid combinations (unauthorized sender role, message type). 91 func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_UnAuthorizedSender() { 92 for _, c := range s.unauthorizedSenderTestCases { 93 str := fmt.Sprintf("role (%s) should not be authorized to send message type (%s) on channel (%s)", c.Identity.Role, c.MessageStr, c.Channel) 94 s.Run(str, func() { 95 pid, err := unittest.PeerIDFromFlowID(c.Identity) 96 require.NoError(s.T(), err) 97 98 authorizedSenderValidator := NewAuthorizedSenderValidator(s.log, s.slashingViolationsConsumer, c.GetIdentity) 99 100 validatePubsub := authorizedSenderValidator.PubSubMessageValidator(c.Channel) 101 pubsubResult := validatePubsub(pid, c.Message) 102 require.Equal(s.T(), p2p.ValidationReject, pubsubResult) 103 }) 104 } 105 } 106 107 // TestValidatorCallback_AuthorizedUnicastOnChannel checks that AuthorizedSenderValidator.Validate does not return an error 108 // for messages sent via unicast that are authorized to be sent via unicast. 109 func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_AuthorizedUnicastOnChannel() { 110 for _, c := range s.authorizedUnicastOnChannel { 111 str := fmt.Sprintf("role (%s) should be authorized to send message type (%s) on channel (%s) via unicast", c.Identity.Role, c.MessageStr, c.Channel) 112 s.Run(str, func() { 113 pid, err := unittest.PeerIDFromFlowID(c.Identity) 114 require.NoError(s.T(), err) 115 116 authorizedSenderValidator := NewAuthorizedSenderValidator(s.log, s.slashingViolationsConsumer, c.GetIdentity) 117 118 msgType, err := authorizedSenderValidator.Validate(pid, c.Message, c.Channel, message.ProtocolUnicast) 119 require.NoError(s.T(), err) 120 require.Equal(s.T(), c.MessageStr, msgType) 121 }) 122 } 123 } 124 125 // TestValidatorCallback_UnAuthorizedUnicastOnChannel checks that AuthorizedSenderValidator.Validate returns message.ErrUnauthorizedUnicastOnChannel 126 // when a message not authorized to be sent via unicast is sent via unicast. 127 func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_UnAuthorizedUnicastOnChannel() { 128 for _, c := range s.unauthorizedUnicastOnChannel { 129 str := fmt.Sprintf("role (%s) should not be authorized to send message type (%s) on channel (%s) via unicast", c.Identity.Role, c.MessageStr, c.Channel) 130 s.Run(str, func() { 131 pid, err := unittest.PeerIDFromFlowID(c.Identity) 132 require.NoError(s.T(), err) 133 134 authorizedSenderValidator := NewAuthorizedSenderValidator(s.log, s.slashingViolationsConsumer, c.GetIdentity) 135 136 msgType, err := authorizedSenderValidator.Validate(pid, c.Message, c.Channel, message.ProtocolUnicast) 137 require.ErrorIs(s.T(), err, message.ErrUnauthorizedUnicastOnChannel) 138 require.Equal(s.T(), c.MessageStr, msgType) 139 }) 140 } 141 } 142 143 // TestValidatorCallback_UnAuthorizedMessageOnChannel checks that for each invalid combination of message type and channel 144 // AuthorizedSenderValidator.Validate returns the appropriate error message.ErrUnauthorizedMessageOnChannel. 145 func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_UnAuthorizedMessageOnChannel() { 146 for _, c := range s.unauthorizedMessageOnChannelTestCases { 147 str := fmt.Sprintf("message type (%s) should not be authorized to be sent on channel (%s)", c.MessageStr, c.Channel) 148 s.Run(str, func() { 149 pid, err := unittest.PeerIDFromFlowID(c.Identity) 150 require.NoError(s.T(), err) 151 152 authorizedSenderValidator := NewAuthorizedSenderValidator(s.log, s.slashingViolationsConsumer, c.GetIdentity) 153 154 msgType, err := authorizedSenderValidator.Validate(pid, c.Message, c.Channel, message.ProtocolUnicast) 155 require.ErrorIs(s.T(), err, message.ErrUnauthorizedMessageOnChannel) 156 require.Equal(s.T(), c.MessageStr, msgType) 157 158 validatePubsub := authorizedSenderValidator.PubSubMessageValidator(c.Channel) 159 pubsubResult := validatePubsub(pid, c.Message) 160 require.Equal(s.T(), p2p.ValidationReject, pubsubResult) 161 }) 162 } 163 } 164 165 // TestValidatorCallback_ClusterPrefixedChannels checks that AuthorizedSenderValidator.Validate correctly 166 // handles cluster prefixed channels during validation. 167 func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_ClusterPrefixedChannels() { 168 identity, _ := unittest.IdentityWithNetworkingKeyFixture(unittest.WithRole(flow.RoleCollection)) 169 clusterID := flow.Localnet 170 171 getIdentityFunc := s.getIdentity(identity) 172 pid, err := unittest.PeerIDFromFlowID(identity) 173 require.NoError(s.T(), err) 174 175 authorizedSenderValidator := NewAuthorizedSenderValidator(s.log, s.slashingViolationsConsumer, getIdentityFunc) 176 177 // ensure ClusterBlockProposal not allowed to be sent on channel via unicast 178 msgType, err := authorizedSenderValidator.Validate(pid, &messages.ClusterBlockProposal{}, channels.ConsensusCluster(clusterID), message.ProtocolUnicast) 179 require.ErrorIs(s.T(), err, message.ErrUnauthorizedUnicastOnChannel) 180 require.Equal(s.T(), message.ClusterBlockProposal, msgType) 181 182 // ensure ClusterBlockProposal is allowed to be sent via pubsub by authorized sender 183 validateCollConsensusPubsub := authorizedSenderValidator.PubSubMessageValidator(channels.ConsensusCluster(clusterID)) 184 pubsubResult := validateCollConsensusPubsub(pid, &messages.ClusterBlockProposal{}) 185 require.Equal(s.T(), p2p.ValidationAccept, pubsubResult) 186 187 // validate collection sync cluster SyncRequest is not allowed to be sent on channel via unicast 188 msgType, err = authorizedSenderValidator.Validate(pid, &messages.SyncRequest{}, channels.SyncCluster(clusterID), message.ProtocolUnicast) 189 require.ErrorIs(s.T(), err, message.ErrUnauthorizedUnicastOnChannel) 190 require.Equal(s.T(), message.SyncRequest, msgType) 191 192 // ensure SyncRequest is allowed to be sent via pubsub by authorized sender 193 validateSyncClusterPubsub := authorizedSenderValidator.PubSubMessageValidator(channels.SyncCluster(clusterID)) 194 pubsubResult = validateSyncClusterPubsub(pid, &messages.SyncRequest{}) 195 require.Equal(s.T(), p2p.ValidationAccept, pubsubResult) 196 } 197 198 // TestValidatorCallback_ValidationFailure checks that AuthorizedSenderValidator.Validate returns the expected validation error. 199 func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_ValidationFailure() { 200 s.Run("sender is ejected", func() { 201 identity, _ := unittest.IdentityWithNetworkingKeyFixture() 202 identity.Ejected = true 203 getIdentityFunc := s.getIdentity(identity) 204 pid, err := unittest.PeerIDFromFlowID(identity) 205 require.NoError(s.T(), err) 206 207 authorizedSenderValidator := NewAuthorizedSenderValidator(s.log, s.slashingViolationsConsumer, getIdentityFunc) 208 209 msgType, err := authorizedSenderValidator.Validate(pid, &messages.SyncRequest{}, channels.SyncCommittee, message.ProtocolUnicast) 210 require.ErrorIs(s.T(), err, ErrSenderEjected) 211 require.Equal(s.T(), "", msgType) 212 213 validatePubsub := authorizedSenderValidator.PubSubMessageValidator(channels.SyncCommittee) 214 pubsubResult := validatePubsub(pid, &messages.SyncRequest{}) 215 require.Equal(s.T(), p2p.ValidationReject, pubsubResult) 216 }) 217 218 s.Run("unknown message type", func() { 219 identity, _ := unittest.IdentityWithNetworkingKeyFixture(unittest.WithRole(flow.RoleConsensus)) 220 type msg struct { 221 *messages.BlockProposal 222 } 223 224 // *validator.msg is not a known message type, but embeds *messages.BlockProposal which is 225 m := &msg{unittest.ProposalFixture()} 226 227 getIdentityFunc := s.getIdentity(identity) 228 pid, err := unittest.PeerIDFromFlowID(identity) 229 require.NoError(s.T(), err) 230 231 authorizedSenderValidator := NewAuthorizedSenderValidator(s.log, s.slashingViolationsConsumer, getIdentityFunc) 232 validatePubsub := authorizedSenderValidator.PubSubMessageValidator(channels.ConsensusCommittee) 233 234 // unknown message types are rejected 235 msgType, err := authorizedSenderValidator.Validate(pid, m, channels.ConsensusCommittee, message.ProtocolUnicast) 236 require.True(s.T(), message.IsUnknownMsgTypeErr(err)) 237 require.Equal(s.T(), "", msgType) 238 pubsubResult := validatePubsub(pid, m) 239 require.Equal(s.T(), p2p.ValidationReject, pubsubResult) 240 241 // nil messages are rejected 242 msgType, err = authorizedSenderValidator.Validate(pid, nil, channels.ConsensusCommittee, message.ProtocolUnicast) 243 require.True(s.T(), message.IsUnknownMsgTypeErr(err)) 244 require.Equal(s.T(), "", msgType) 245 pubsubResult = validatePubsub(pid, nil) 246 require.Equal(s.T(), p2p.ValidationReject, pubsubResult) 247 }) 248 249 s.Run("sender is not staked getIdentityFunc does not return identity ", func() { 250 identity, _ := unittest.IdentityWithNetworkingKeyFixture() 251 252 // getIdentityFunc simulates unstaked node not found in participant list 253 getIdentityFunc := func(id peer.ID) (*flow.Identity, bool) { return nil, false } 254 255 pid, err := unittest.PeerIDFromFlowID(identity) 256 require.NoError(s.T(), err) 257 258 authorizedSenderValidator := NewAuthorizedSenderValidator(s.log, s.slashingViolationsConsumer, getIdentityFunc) 259 260 msgType, err := authorizedSenderValidator.Validate(pid, &messages.SyncRequest{}, channels.SyncCommittee, message.ProtocolUnicast) 261 require.ErrorIs(s.T(), err, ErrIdentityUnverified) 262 require.Equal(s.T(), "", msgType) 263 264 validatePubsub := authorizedSenderValidator.PubSubMessageValidator(channels.SyncCommittee) 265 pubsubResult := validatePubsub(pid, &messages.SyncRequest{}) 266 require.Equal(s.T(), p2p.ValidationReject, pubsubResult) 267 }) 268 } 269 270 // TestValidatorCallback_ValidationFailure checks that AuthorizedSenderValidator returns the expected validation error when a unicast-only message is published. 271 func (s *TestAuthorizedSenderValidatorSuite) TestValidatorCallback_UnauthorizedPublishOnChannel() { 272 for _, c := range s.authorizedUnicastOnChannel { 273 str := fmt.Sprintf("message type (%s) is not authorized to be sent via libp2p publish", c.MessageStr) 274 s.Run(str, func() { 275 pid, err := unittest.PeerIDFromFlowID(c.Identity) 276 require.NoError(s.T(), err) 277 278 authorizedSenderValidator := NewAuthorizedSenderValidator(s.log, s.slashingViolationsConsumer, c.GetIdentity) 279 280 msgType, err := authorizedSenderValidator.Validate(pid, c.Message, c.Channel, message.ProtocolPublish) 281 if c.MessageStr == message.TestMessage { 282 require.NoError(s.T(), err) 283 } else { 284 require.ErrorIs(s.T(), err, message.ErrUnauthorizedPublishOnChannel) 285 require.Equal(s.T(), c.MessageStr, msgType) 286 } 287 }) 288 } 289 } 290 291 // initializeAuthorizationTestCases initializes happy and sad path test cases for checking authorized and unauthorized role message combinations. 292 func (s *TestAuthorizedSenderValidatorSuite) initializeAuthorizationTestCases() { 293 for _, c := range s.allMsgConfigs { 294 for channel, channelAuthConfig := range c.Config { 295 for _, role := range flow.Roles() { 296 identity, _ := unittest.IdentityWithNetworkingKeyFixture(unittest.WithRole(role)) 297 tc := TestCase{ 298 Identity: identity, 299 GetIdentity: s.getIdentity(identity), 300 Channel: channel, 301 Message: c.Type(), 302 MessageStr: c.Name, 303 Protocols: channelAuthConfig.AllowedProtocols, 304 } 305 if channelAuthConfig.AuthorizedRoles.Contains(role) { 306 // test cases for validation success happy path 307 s.authorizedSenderTestCases = append(s.authorizedSenderTestCases, tc) 308 } else { 309 // test cases for validation unsuccessful sad path 310 s.unauthorizedSenderTestCases = append(s.unauthorizedSenderTestCases, tc) 311 } 312 } 313 } 314 } 315 } 316 317 // initializeInvalidMessageOnChannelTestCases initializes test cases for all possible combinations of invalid message types on channel. 318 // NOTE: the role in the test case does not matter since ErrUnauthorizedMessageOnChannel will be returned before the role is checked. 319 func (s *TestAuthorizedSenderValidatorSuite) initializeInvalidMessageOnChannelTestCases() { 320 // iterate all channels 321 for _, c := range s.allMsgConfigs { 322 for channel, channelAuthConfig := range c.Config { 323 identity, _ := unittest.IdentityWithNetworkingKeyFixture(unittest.WithRole(channelAuthConfig.AuthorizedRoles[0])) 324 325 // iterate all message types 326 for _, config := range s.allMsgConfigs { 327 // include test if message type is not authorized on channel 328 _, ok := config.Config[channel] 329 if config.Name != c.Name && !ok { 330 tc := TestCase{ 331 Identity: identity, 332 GetIdentity: s.getIdentity(identity), 333 Channel: channel, 334 Message: config.Type(), 335 MessageStr: config.Name, 336 Protocols: channelAuthConfig.AllowedProtocols, 337 } 338 s.unauthorizedMessageOnChannelTestCases = append(s.unauthorizedMessageOnChannelTestCases, tc) 339 } 340 } 341 } 342 } 343 } 344 345 // initializeUnicastOnChannelTestCases initializes happy and sad path test cases for unicast on channel message combinations. 346 func (s *TestAuthorizedSenderValidatorSuite) initializeUnicastOnChannelTestCases() { 347 for _, c := range s.allMsgConfigs { 348 for channel, channelAuthConfig := range c.Config { 349 identity, _ := unittest.IdentityWithNetworkingKeyFixture(unittest.WithRole(channelAuthConfig.AuthorizedRoles[0])) 350 tc := TestCase{ 351 Identity: identity, 352 GetIdentity: s.getIdentity(identity), 353 Channel: channel, 354 Message: c.Type(), 355 MessageStr: c.Name, 356 Protocols: channelAuthConfig.AllowedProtocols, 357 } 358 if channelAuthConfig.AllowedProtocols.Contains(message.ProtocolUnicast) { 359 s.authorizedUnicastOnChannel = append(s.authorizedUnicastOnChannel, tc) 360 } else { 361 s.unauthorizedUnicastOnChannel = append(s.unauthorizedUnicastOnChannel, tc) 362 } 363 } 364 } 365 } 366 367 // getIdentity returns a callback that simply returns the provided identity. 368 func (s *TestAuthorizedSenderValidatorSuite) getIdentity(id *flow.Identity) func(pid peer.ID) (*flow.Identity, bool) { 369 return func(pid peer.ID) (*flow.Identity, bool) { 370 return id, true 371 } 372 }