github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/p2p/pubsub_filter.go (about)

     1  package p2p
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/libp2p/go-libp2p-core/peer"
     8  	pubsub "github.com/libp2p/go-libp2p-pubsub"
     9  	pubsubpb "github.com/libp2p/go-libp2p-pubsub/pb"
    10  	"github.com/prysmaticlabs/prysm/beacon-chain/p2p/encoder"
    11  )
    12  
    13  var _ pubsub.SubscriptionFilter = (*Service)(nil)
    14  
    15  const pubsubSubscriptionRequestLimit = 100
    16  
    17  // CanSubscribe returns true if the topic is of interest and we could subscribe to it.
    18  func (s *Service) CanSubscribe(topic string) bool {
    19  	if !s.isInitialized() {
    20  		return false
    21  	}
    22  	parts := strings.Split(topic, "/")
    23  	if len(parts) != 5 {
    24  		return false
    25  	}
    26  	// The topic must start with a slash, which means the first part will be empty.
    27  	if parts[0] != "" {
    28  		return false
    29  	}
    30  	if parts[1] != "eth2" {
    31  		return false
    32  	}
    33  	fd, err := s.forkDigest()
    34  	if err != nil {
    35  		log.WithError(err).Error("Could not determine fork digest")
    36  		return false
    37  	}
    38  	if parts[2] != fmt.Sprintf("%x", fd) {
    39  		return false
    40  	}
    41  	if parts[4] != encoder.ProtocolSuffixSSZSnappy {
    42  		return false
    43  	}
    44  
    45  	// Check the incoming topic matches any topic mapping. This includes a check for part[3].
    46  	for gt := range GossipTopicMappings {
    47  		if _, err := scanfcheck(strings.Join(parts[0:4], "/"), gt); err == nil {
    48  			return true
    49  		}
    50  	}
    51  
    52  	return false
    53  }
    54  
    55  // FilterIncomingSubscriptions is invoked for all RPCs containing subscription notifications.
    56  // This method returns only the topics of interest and may return an error if the subscription
    57  // request contains too many topics.
    58  func (s *Service) FilterIncomingSubscriptions(_ peer.ID, subs []*pubsubpb.RPC_SubOpts) ([]*pubsubpb.RPC_SubOpts, error) {
    59  	if len(subs) > pubsubSubscriptionRequestLimit {
    60  		return nil, pubsub.ErrTooManySubscriptions
    61  	}
    62  
    63  	return pubsub.FilterSubscriptions(subs, s.CanSubscribe), nil
    64  }
    65  
    66  // scanfcheck uses fmt.Sscanf to check that a given string matches expected format. This method
    67  // returns the number of formatting substitutions matched and error if the string does not match
    68  // the expected format. Note: this method only accepts integer compatible formatting substitutions
    69  // such as %d or %x.
    70  func scanfcheck(input, format string) (int, error) {
    71  	var t int
    72  	// Sscanf requires argument pointers with the appropriate type to load the value from the input.
    73  	// This method only checks that the input conforms to the format, the arguments are not used and
    74  	// therefore we can reuse the same integer pointer.
    75  	var cnt = strings.Count(format, "%")
    76  	var args []interface{}
    77  	for i := 0; i < cnt; i++ {
    78  		args = append(args, &t)
    79  	}
    80  	return fmt.Sscanf(input, format, args...)
    81  }