github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/channels/channels.go (about)

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