github.com/onflow/flow-go@v0.33.17/network/channels/channels.go (about) 1 // (c) 2019 Dapper Labs - ALL RIGHTS RESERVED 2 3 package channels 4 5 import ( 6 "fmt" 7 "strings" 8 9 "github.com/onflow/flow-go/model/flow" 10 ) 11 12 // init is called first time this package is imported. 13 // It creates and initializes channelRoleMap and clusterChannelPrefixRoleMap. 14 func init() { 15 initializeChannelRoleMap() 16 } 17 18 // channelRoleMap keeps a map between channels and the list of flow roles involved in them. 19 var channelRoleMap map[Channel]flow.RoleList 20 21 // clusterChannelPrefixRoleMap keeps a map between cluster channel prefixes and the list of flow roles involved in them. 22 var clusterChannelPrefixRoleMap map[string]flow.RoleList 23 24 // RolesByChannel returns list of flow roles involved in the channel. 25 // If the given channel is a public channel, the returned list will 26 // contain all roles. 27 func RolesByChannel(channel Channel) (flow.RoleList, bool) { 28 if IsClusterChannel(channel) { 29 return ClusterChannelRoles(channel), true 30 } 31 if IsPublicChannel(channel) { 32 return flow.Roles(), true 33 } 34 roles, ok := channelRoleMap[channel] 35 return roles, ok 36 } 37 38 // ChannelExists returns true if the channel exists. 39 func ChannelExists(channel Channel) bool { 40 if _, ok := RolesByChannel(channel); ok { 41 return true 42 } 43 44 return false 45 } 46 47 // ChannelsByRole returns a list of all channels the role subscribes to (except cluster-based channels and public channels). 48 func ChannelsByRole(role flow.Role) ChannelList { 49 channels := make(ChannelList, 0) 50 for channel, roles := range channelRoleMap { 51 if roles.Contains(role) { 52 channels = append(channels, channel) 53 } 54 } 55 56 return channels 57 } 58 59 // UniqueChannels returns list of non-cluster channels with a unique RoleList accompanied 60 // with the list of all cluster channels. 61 // e.g. if channel X and Y both are non-cluster channels and have role IDs [A,B,C] then only one of them will be in the returned list. 62 func UniqueChannels(channels ChannelList) ChannelList { 63 // uniques keeps the set of unique channels based on their RoleList. 64 uniques := make(ChannelList, 0) 65 // added keeps track of channels added to uniques for deduplication. 66 added := make(map[flow.Identifier]struct{}) 67 68 // a channel is added to uniques if it is either a 69 // cluster channel, or no non-cluster channel with the same set of roles 70 // has already been added to uniques. 71 // We use identifier of RoleList to determine its uniqueness. 72 for _, channel := range channels { 73 // non-cluster channel deduplicated based identifier of role list 74 if !IsClusterChannel(channel) { 75 id := channelRoleMap[channel].ID() 76 if _, ok := added[id]; ok { 77 // a channel with same RoleList already added, hence skips 78 continue 79 } 80 added[id] = struct{}{} 81 } 82 83 uniques = append(uniques, channel) 84 } 85 86 return uniques 87 } 88 89 // Channels returns all channels that nodes of any role have subscribed to (except cluster-based channels). 90 func Channels() ChannelList { 91 channels := make(ChannelList, 0) 92 for channel := range channelRoleMap { 93 channels = append(channels, channel) 94 } 95 channels = append(channels, PublicChannels()...) 96 97 return channels 98 } 99 100 // PublicChannels returns all channels that are used on the public network. 101 func PublicChannels() ChannelList { 102 return ChannelList{ 103 PublicSyncCommittee, 104 PublicReceiveBlocks, 105 PublicExecutionDataService, 106 } 107 } 108 109 // IsPublicChannel returns true if channel is in the public channels list 110 func IsPublicChannel(channel Channel) bool { 111 return PublicChannels().Contains(channel) 112 } 113 114 // channels 115 const ( 116 117 // Channels used for testing 118 TestNetworkChannel = Channel("test-network") 119 TestMetricsChannel = Channel("test-metrics") 120 121 // Channels for consensus protocols 122 ConsensusCommittee = Channel("consensus-committee") 123 ConsensusClusterPrefix = "consensus-cluster" // dynamic channel, use ConsensusCluster function 124 125 // Channels for protocols actively synchronizing state across nodes 126 SyncCommittee = Channel("sync-committee") 127 SyncClusterPrefix = "sync-cluster" // dynamic channel, use SyncCluster function 128 129 // Channels for dkg communication 130 DKGCommittee = "dkg-committee" 131 132 // Channels for actively pushing entities to subscribers 133 PushTransactions = Channel("push-transactions") 134 PushGuarantees = Channel("push-guarantees") 135 PushBlocks = Channel("push-blocks") 136 PushReceipts = Channel("push-receipts") 137 PushApprovals = Channel("push-approvals") 138 139 // Channels for actively requesting missing entities 140 RequestCollections = Channel("request-collections") 141 RequestChunks = Channel("request-chunks") 142 RequestReceiptsByBlockID = Channel("request-receipts-by-block-id") 143 RequestApprovalsByChunk = Channel("request-approvals-by-chunk") 144 145 // Channel aliases to make the code more readable / more robust to errors 146 ReceiveTransactions = PushTransactions 147 ReceiveGuarantees = PushGuarantees 148 ReceiveBlocks = PushBlocks 149 ReceiveReceipts = PushReceipts 150 ReceiveApprovals = PushApprovals 151 152 ProvideCollections = RequestCollections 153 ProvideChunks = RequestChunks 154 ProvideReceiptsByBlockID = RequestReceiptsByBlockID 155 ProvideApprovalsByChunk = RequestApprovalsByChunk 156 157 // Public network channels 158 PublicPushBlocks = Channel("public-push-blocks") 159 PublicReceiveBlocks = PublicPushBlocks 160 PublicSyncCommittee = Channel("public-sync-committee") 161 PublicExecutionDataService = Channel("public-execution-data-service") 162 163 // Execution data service 164 ExecutionDataService = Channel("execution-data-service") 165 ) 166 167 // initializeChannelRoleMap initializes an instance of channelRoleMap and populates it 168 // with the channels and their corresponding list of authorized roles. 169 // Note: Please update this map, if a new channel is defined or a the roles subscribing to a channel have changed 170 func initializeChannelRoleMap() { 171 channelRoleMap = make(map[Channel]flow.RoleList) 172 173 // Channels for test 174 channelRoleMap[TestNetworkChannel] = flow.RoleList{flow.RoleCollection, flow.RoleConsensus, flow.RoleExecution, 175 flow.RoleVerification, flow.RoleAccess} 176 channelRoleMap[TestMetricsChannel] = flow.RoleList{flow.RoleCollection, flow.RoleConsensus, flow.RoleExecution, 177 flow.RoleVerification, flow.RoleAccess} 178 179 // Channels for consensus protocols 180 channelRoleMap[ConsensusCommittee] = flow.RoleList{flow.RoleConsensus} 181 182 // Channels for protocols actively synchronizing state across nodes 183 channelRoleMap[SyncCommittee] = flow.Roles() 184 185 // Channels for DKG communication 186 channelRoleMap[DKGCommittee] = flow.RoleList{flow.RoleConsensus} 187 188 // Channels for actively pushing entities to subscribers 189 channelRoleMap[PushTransactions] = flow.RoleList{flow.RoleCollection} 190 channelRoleMap[PushGuarantees] = flow.RoleList{flow.RoleCollection, flow.RoleConsensus} 191 channelRoleMap[PushBlocks] = flow.RoleList{flow.RoleCollection, flow.RoleConsensus, flow.RoleExecution, 192 flow.RoleVerification, flow.RoleAccess} 193 channelRoleMap[PushReceipts] = flow.RoleList{flow.RoleConsensus, flow.RoleExecution, flow.RoleVerification, 194 flow.RoleAccess} 195 channelRoleMap[PushApprovals] = flow.RoleList{flow.RoleConsensus, flow.RoleVerification} 196 197 // Channels for actively requesting missing entities 198 channelRoleMap[RequestCollections] = flow.RoleList{flow.RoleCollection, flow.RoleExecution, flow.RoleAccess} 199 channelRoleMap[RequestChunks] = flow.RoleList{flow.RoleExecution, flow.RoleVerification} 200 channelRoleMap[RequestReceiptsByBlockID] = flow.RoleList{flow.RoleConsensus, flow.RoleExecution} 201 channelRoleMap[RequestApprovalsByChunk] = flow.RoleList{flow.RoleConsensus, flow.RoleVerification} 202 203 // Channel aliases to make the code more readable / more robust to errors 204 channelRoleMap[ReceiveGuarantees] = flow.RoleList{flow.RoleCollection, flow.RoleConsensus} 205 channelRoleMap[ReceiveBlocks] = flow.RoleList{flow.RoleCollection, flow.RoleConsensus, flow.RoleExecution, 206 flow.RoleVerification, flow.RoleAccess} 207 channelRoleMap[ReceiveReceipts] = flow.RoleList{flow.RoleConsensus, flow.RoleExecution, flow.RoleVerification, 208 flow.RoleAccess} 209 channelRoleMap[ReceiveApprovals] = flow.RoleList{flow.RoleConsensus, flow.RoleVerification} 210 211 channelRoleMap[ProvideCollections] = flow.RoleList{flow.RoleCollection, flow.RoleExecution, flow.RoleAccess} 212 channelRoleMap[ProvideChunks] = flow.RoleList{flow.RoleExecution, flow.RoleVerification} 213 channelRoleMap[ProvideReceiptsByBlockID] = flow.RoleList{flow.RoleConsensus, flow.RoleExecution} 214 channelRoleMap[ProvideApprovalsByChunk] = flow.RoleList{flow.RoleConsensus, flow.RoleVerification} 215 216 clusterChannelPrefixRoleMap = make(map[string]flow.RoleList) 217 218 clusterChannelPrefixRoleMap[SyncClusterPrefix] = flow.RoleList{flow.RoleCollection} 219 clusterChannelPrefixRoleMap[ConsensusClusterPrefix] = flow.RoleList{flow.RoleCollection} 220 } 221 222 // ClusterChannelRoles returns the list of roles that are involved in the given cluster-based channel. 223 func ClusterChannelRoles(clusterChannel Channel) flow.RoleList { 224 if prefix, ok := ClusterChannelPrefix(clusterChannel); ok { 225 return clusterChannelPrefixRoleMap[prefix] 226 } 227 228 return flow.RoleList{} 229 } 230 231 // ClusterChannelPrefix returns the cluster channel prefix and true if clusterChannel exists inclusterChannelPrefixRoleMap 232 func ClusterChannelPrefix(clusterChannel Channel) (string, bool) { 233 for prefix := range clusterChannelPrefixRoleMap { 234 if strings.HasPrefix(clusterChannel.String(), prefix) { 235 return prefix, true 236 } 237 } 238 239 return "", false 240 } 241 242 // IsClusterChannel returns true if channel is cluster-based. 243 // Currently, only collection nodes are involved in a cluster-based channels. 244 func IsClusterChannel(channel Channel) bool { 245 _, ok := ClusterChannelPrefix(channel) 246 return ok 247 } 248 249 // TopicFromChannel returns the unique LibP2P topic form the channel. 250 // The channel is made up of name string suffixed with root block id. 251 // The root block id is used to prevent cross talks between nodes on different sporks. 252 func TopicFromChannel(channel Channel, rootBlockID flow.Identifier) Topic { 253 // skip root block suffix, if this is a cluster specific channel. A cluster specific channel is inherently 254 // unique for each epoch 255 if IsClusterChannel(channel) { 256 return Topic(channel) 257 } 258 return Topic(fmt.Sprintf("%s/%s", string(channel), rootBlockID.String())) 259 } 260 261 // TopicsFromChannels returns the unique LibP2P topics form the channels. 262 func TopicsFromChannels(channels ChannelList, rootBlockID flow.Identifier) []Topic { 263 topics := make([]Topic, 0, len(channels)) 264 for _, channel := range channels { 265 topics = append(topics, TopicFromChannel(channel, rootBlockID)) 266 } 267 return topics 268 } 269 270 func ChannelFromTopic(topic Topic) (Channel, bool) { 271 if IsClusterChannel(Channel(topic)) { 272 return Channel(topic), true 273 } 274 275 if index := strings.LastIndex(topic.String(), "/"); index != -1 { 276 return Channel(topic[:index]), true 277 } 278 279 return "", false 280 } 281 282 // sporkIdFromTopic returns the pre-pended spork ID flow identifier for the topic. 283 // A valid channel has a spork ID suffix: 284 // 285 // channel/spork_id 286 // 287 // A generic error is returned if an error is encountered while converting the spork ID to flow Identifier or 288 // the spork ID is missing. 289 func sporkIdFromTopic(topic Topic) (flow.Identifier, error) { 290 if index := strings.LastIndex(topic.String(), "/"); index != -1 { 291 id, err := flow.HexStringToIdentifier(string(topic)[index+1:]) 292 if err != nil { 293 return flow.Identifier{}, fmt.Errorf("failed to get spork ID from topic %s: %w", topic, err) 294 } 295 296 return id, nil 297 } 298 return flow.Identifier{}, fmt.Errorf("spork id missing from topic") 299 } 300 301 // sporkIdStrFromTopic returns the pre-pended spork ID string for the topic. 302 // A valid channel has a spork ID suffix: 303 // 304 // channel/spork_id 305 // 306 // A generic error is returned if an error is encountered while deriving the spork ID from the topic 307 func sporkIdStrFromTopic(topic Topic) (string, error) { 308 sporkId, err := sporkIdFromTopic(topic) 309 if err != nil { 310 return "", err 311 } 312 return sporkId.String(), nil 313 } 314 315 // clusterIDStrFromTopic returns the appended cluster ID in flow.ChainID format for the cluster prefixed topic. 316 // A valid cluster-prefixed channel includes the cluster prefix and cluster ID suffix: 317 // 318 // sync-cluster/some_cluster_id 319 // 320 // A generic error is returned if the topic is malformed. 321 func clusterIDStrFromTopic(topic Topic) (flow.ChainID, error) { 322 for prefix := range clusterChannelPrefixRoleMap { 323 if strings.HasPrefix(topic.String(), prefix) { 324 return flow.ChainID(strings.TrimPrefix(topic.String(), fmt.Sprintf("%s-", prefix))), nil 325 } 326 } 327 return "", fmt.Errorf("failed to get cluster ID from topic %s", topic) 328 } 329 330 // ConsensusCluster returns a dynamic cluster consensus channel based on 331 // the chain ID of the cluster in question. 332 func ConsensusCluster(clusterID flow.ChainID) Channel { 333 return Channel(fmt.Sprintf("%s-%s", ConsensusClusterPrefix, clusterID)) 334 } 335 336 // SyncCluster returns a dynamic cluster sync channel based on the chain 337 // ID of the cluster in question. 338 func SyncCluster(clusterID flow.ChainID) Channel { 339 return Channel(fmt.Sprintf("%s-%s", SyncClusterPrefix, clusterID)) 340 } 341 342 // IsValidNonClusterFlowTopic ensures the topic is a valid Flow network topic and 343 // ensures the sporkID part of the Topic is equal to the current network sporkID. 344 // Expected errors: 345 // - InvalidTopicErr if the topic is not a if the topic is not a valid topic for the given spork. 346 func IsValidNonClusterFlowTopic(topic Topic, expectedSporkID flow.Identifier) error { 347 sporkID, err := sporkIdStrFromTopic(topic) 348 if err != nil { 349 return NewInvalidTopicErr(topic, fmt.Errorf("failed to get spork ID from topic: %w", err)) 350 } 351 352 if sporkID != expectedSporkID.String() { 353 return NewInvalidTopicErr(topic, fmt.Errorf("invalid flow topic mismatch spork ID expected spork ID %s actual spork ID %s", expectedSporkID, sporkID)) 354 } 355 356 return isValidFlowTopic(topic) 357 } 358 359 // IsValidFlowClusterTopic ensures the topic is a valid Flow network topic and 360 // ensures the cluster ID part of the Topic is equal to one of the provided active cluster IDs. 361 // All errors returned from this function can be considered benign. 362 // Expected errors: 363 // - InvalidTopicErr if the topic is not a valid Flow topic or the cluster ID cannot be derived from the topic. 364 // - UnknownClusterIDErr if the cluster ID from the topic is not in the activeClusterIDS list. 365 func IsValidFlowClusterTopic(topic Topic, activeClusterIDS flow.ChainIDList) error { 366 err := isValidFlowTopic(topic) 367 if err != nil { 368 return err 369 } 370 371 clusterID, err := clusterIDStrFromTopic(topic) 372 if err != nil { 373 return NewInvalidTopicErr(topic, fmt.Errorf("failed to get cluster ID from topic: %w", err)) 374 } 375 376 for _, activeClusterID := range activeClusterIDS { 377 if clusterID == activeClusterID { 378 return nil 379 } 380 } 381 382 return NewUnknownClusterIdErr(clusterID, activeClusterIDS) 383 } 384 385 // isValidFlowTopic ensures the topic is a valid Flow network topic. 386 // A valid Topic has the following properties: 387 // - A Channel can be derived from the Topic and that channel exists. 388 // Expected errors: 389 // - InvalidTopicErr if the topic is not a valid Flow topic. 390 func isValidFlowTopic(topic Topic) error { 391 channel, ok := ChannelFromTopic(topic) 392 if !ok { 393 return NewInvalidTopicErr(topic, fmt.Errorf("invalid topic: failed to get channel from topic")) 394 } 395 err := IsValidFlowChannel(channel) 396 if err != nil { 397 return NewInvalidTopicErr(topic, fmt.Errorf("invalid topic: %w", err)) 398 } 399 return nil 400 } 401 402 // IsValidFlowChannel ensures the channel is a valid Flow network channel. 403 // All errors returned from this function can be considered benign. 404 func IsValidFlowChannel(channel Channel) error { 405 if !ChannelExists(channel) { 406 return fmt.Errorf("unknown channel: %s", channel) 407 } 408 return nil 409 }