github.com/koko1123/flow-go-1@v0.29.6/network/p2p/scoring/score_option.go (about)

     1  package scoring
     2  
     3  import (
     4  	"time"
     5  
     6  	pubsub "github.com/libp2p/go-libp2p-pubsub"
     7  	"github.com/libp2p/go-libp2p/core/peer"
     8  	"github.com/rs/zerolog"
     9  
    10  	"github.com/koko1123/flow-go-1/model/flow"
    11  	"github.com/koko1123/flow-go-1/module"
    12  	"github.com/koko1123/flow-go-1/utils/logging"
    13  )
    14  
    15  const (
    16  	DefaultPeerScoringEnabled = true // enable peer scoring by default on node builder
    17  
    18  	DefaultAppSpecificScoreWeight = 1
    19  	MaxAppSpecificPenalty         = -100
    20  	MinAppSpecificPenalty         = -1
    21  	MaxAppSpecificReward          = 100
    22  
    23  	// DefaultGossipThreshold when a peer's score drops below this threshold,
    24  	// no gossip is emitted towards that peer and gossip from that peer is ignored.
    25  	//
    26  	// Validation Constraint: GossipThreshold >= PublishThreshold && GossipThreshold < 0
    27  	//
    28  	// How we use it:
    29  	// As current max penalty is -100, we set the threshold to -99 so that all gossips
    30  	// to and from peers with score -100 are ignored.
    31  	DefaultGossipThreshold = -99
    32  
    33  	// DefaultPublishThreshold when a peer's score drops below this threshold,
    34  	// self-published messages are not propagated towards this peer.
    35  	//
    36  	// Validation Constraint:
    37  	// PublishThreshold >= GraylistThreshold && PublishThreshold <= GossipThreshold && PublishThreshold < 0.
    38  	//
    39  	// How we use it:
    40  	// As current max penalty is -100, we set the threshold to -99 so that all penalized peers are deprived of
    41  	// receiving any published messages.
    42  	DefaultPublishThreshold = -99
    43  
    44  	// DefaultGraylistThreshold when a peer's score drops below this threshold, the peer is graylisted, i.e.,
    45  	// incoming RPCs from the peer are ignored.
    46  	//
    47  	// Validation Constraint:
    48  	// GraylistThreshold =< PublishThreshold && GraylistThreshold =< GossipThreshold && GraylistThreshold < 0
    49  	//
    50  	// How we use it:
    51  	// As current max penalty is -100, we set the threshold to -99 so that all penalized peers are graylisted.
    52  	DefaultGraylistThreshold = -99
    53  
    54  	// DefaultAcceptPXThreshold when a peer sends us PX information with a prune, we only accept it and connect to the supplied
    55  	// peers if the originating peer's score exceeds this threshold.
    56  	//
    57  	// Validation Constraint: must be non-negative.
    58  	//
    59  	// How we use it:
    60  	// As current max reward is 100, we set the threshold to 99 so that we only receive supplied peers from
    61  	// well-behaved peers.
    62  	DefaultAcceptPXThreshold = 99
    63  
    64  	// DefaultOpportunisticGraftThreshold when the median peer score in the mesh drops below this value,
    65  	// the peer may select more peers with score above the median to opportunistically graft on the mesh.
    66  	//
    67  	// Validation Constraint: must be non-negative.
    68  	//
    69  	// How we use it:
    70  	// We set it to the MaxAppSpecificReward + 1 so that we only opportunistically graft peers that are not access nodes (i.e., with MinAppSpecificPenalty),
    71  	// or penalized peers (i.e., with MaxAppSpecificPenalty).
    72  	DefaultOpportunisticGraftThreshold = MaxAppSpecificReward + 1
    73  
    74  	// MaxDebugLogs sets the max number of debug/trace log events per second. Logs emitted above
    75  	// this threshold are dropped.
    76  	MaxDebugLogs = 50
    77  )
    78  
    79  // ScoreOption is a functional option for configuring the peer scoring system.
    80  type ScoreOption struct {
    81  	logger                   zerolog.Logger
    82  	validator                *SubscriptionValidator
    83  	idProvider               module.IdentityProvider
    84  	peerScoreParams          *pubsub.PeerScoreParams
    85  	peerThresholdParams      *pubsub.PeerScoreThresholds
    86  	appSpecificScoreFunction func(peer.ID) float64
    87  }
    88  
    89  type PeerScoreParamsOption func(option *ScoreOption)
    90  
    91  func WithAppSpecificScoreFunction(appSpecificScoreFunction func(peer.ID) float64) PeerScoreParamsOption {
    92  	return func(s *ScoreOption) {
    93  		s.appSpecificScoreFunction = appSpecificScoreFunction
    94  	}
    95  }
    96  
    97  func NewScoreOption(logger zerolog.Logger, idProvider module.IdentityProvider, opts ...PeerScoreParamsOption) *ScoreOption {
    98  	throttledSampler := logging.BurstSampler(MaxDebugLogs, time.Second)
    99  	logger = logger.With().
   100  		Str("module", "pubsub_score_option").
   101  		Logger().
   102  		Sample(zerolog.LevelSampler{
   103  			TraceSampler: throttledSampler,
   104  			DebugSampler: throttledSampler,
   105  		})
   106  	validator := NewSubscriptionValidator()
   107  	s := &ScoreOption{
   108  		logger:                   logger,
   109  		validator:                validator,
   110  		idProvider:               idProvider,
   111  		appSpecificScoreFunction: defaultAppSpecificScoreFunction(logger, idProvider, validator),
   112  	}
   113  
   114  	for _, opt := range opts {
   115  		opt(s)
   116  	}
   117  
   118  	return s
   119  }
   120  
   121  func (s *ScoreOption) SetSubscriptionProvider(provider *SubscriptionProvider) {
   122  	s.validator.RegisterSubscriptionProvider(provider)
   123  }
   124  
   125  func (s *ScoreOption) BuildFlowPubSubScoreOption() pubsub.Option {
   126  	s.preparePeerScoreParams()
   127  	s.preparePeerScoreThresholds()
   128  
   129  	s.logger.Info().
   130  		Float64("gossip_threshold", s.peerThresholdParams.GossipThreshold).
   131  		Float64("publish_threshold", s.peerThresholdParams.PublishThreshold).
   132  		Float64("graylist_threshold", s.peerThresholdParams.GraylistThreshold).
   133  		Float64("accept_px_threshold", s.peerThresholdParams.AcceptPXThreshold).
   134  		Float64("opportunistic_graft_threshold", s.peerThresholdParams.OpportunisticGraftThreshold).
   135  		Msg("peer score thresholds configured")
   136  
   137  	return pubsub.WithPeerScore(
   138  		s.peerScoreParams,
   139  		s.peerThresholdParams,
   140  	)
   141  }
   142  
   143  func (s *ScoreOption) preparePeerScoreThresholds() {
   144  	s.peerThresholdParams = &pubsub.PeerScoreThresholds{
   145  		GossipThreshold:             DefaultGossipThreshold,
   146  		PublishThreshold:            DefaultPublishThreshold,
   147  		GraylistThreshold:           DefaultGraylistThreshold,
   148  		AcceptPXThreshold:           DefaultAcceptPXThreshold,
   149  		OpportunisticGraftThreshold: DefaultOpportunisticGraftThreshold,
   150  	}
   151  }
   152  
   153  // preparePeerScoreParams prepares the peer score parameters for the pubsub system.
   154  // It is based on the default parameters defined in libp2p pubsub peer scoring.
   155  func (s *ScoreOption) preparePeerScoreParams() {
   156  	s.peerScoreParams = &pubsub.PeerScoreParams{
   157  		// we don't set all the parameters, so we skip the atomic validation.
   158  		// atomic validation fails initialization if any parameter is not set.
   159  		SkipAtomicValidation: true,
   160  
   161  		// DecayInterval is the interval over which we decay the effect of past behavior. So that
   162  		// a good or bad behavior will not have a permanent effect on the score.
   163  		DecayInterval: time.Hour,
   164  
   165  		// DecayToZero defines the maximum value below which a peer scoring counter is reset to zero.
   166  		// This is to prevent the counter from decaying to a very small value.
   167  		// The default value is 0.01, which means that a counter will be reset to zero if it decays to 0.01.
   168  		// When a counter hits the DecayToZero threshold, it means that the peer did not exhibit the behavior
   169  		// for a long time, and we can reset the counter.
   170  		DecayToZero: 0.01,
   171  
   172  		// AppSpecificScore is a function that takes a peer ID and returns an application specific score.
   173  		// At the current stage, we only use it to penalize and reward the peers based on their subscriptions.
   174  		AppSpecificScore: s.appSpecificScoreFunction,
   175  		// AppSpecificWeight is the weight of the application specific score.
   176  		AppSpecificWeight: DefaultAppSpecificScoreWeight,
   177  	}
   178  }
   179  
   180  func (s *ScoreOption) BuildGossipSubScoreOption() pubsub.Option {
   181  	s.preparePeerScoreParams()
   182  	s.preparePeerScoreThresholds()
   183  
   184  	s.logger.Info().
   185  		Float64("gossip_threshold", s.peerThresholdParams.GossipThreshold).
   186  		Float64("publish_threshold", s.peerThresholdParams.PublishThreshold).
   187  		Float64("graylist_threshold", s.peerThresholdParams.GraylistThreshold).
   188  		Float64("accept_px_threshold", s.peerThresholdParams.AcceptPXThreshold).
   189  		Float64("opportunistic_graft_threshold", s.peerThresholdParams.OpportunisticGraftThreshold).
   190  		Msg("peer score thresholds configured")
   191  
   192  	return pubsub.WithPeerScore(
   193  		s.peerScoreParams,
   194  		s.peerThresholdParams,
   195  	)
   196  }
   197  
   198  func defaultAppSpecificScoreFunction(logger zerolog.Logger, idProvider module.IdentityProvider, validator *SubscriptionValidator) func(peer.ID) float64 {
   199  	return func(pid peer.ID) float64 {
   200  		lg := logger.With().Str("peer_id", pid.String()).Logger()
   201  
   202  		// checks if peer has a valid Flow protocol identity.
   203  		flowId, err := HasValidFlowIdentity(idProvider, pid)
   204  		if err != nil {
   205  			lg.Error().
   206  				Err(err).
   207  				Bool(logging.KeySuspicious, true).
   208  				Msg("invalid peer identity, penalizing peer")
   209  			return MaxAppSpecificPenalty
   210  		}
   211  
   212  		lg = lg.With().
   213  			Hex("flow_id", logging.ID(flowId.NodeID)).
   214  			Str("role", flowId.Role.String()).
   215  			Logger()
   216  
   217  		// checks if peer has any subscription violation.
   218  		if err := validator.CheckSubscribedToAllowedTopics(pid, flowId.Role); err != nil {
   219  			lg.Err(err).
   220  				Bool(logging.KeySuspicious, true).
   221  				Msg("invalid subscription detected, penalizing peer")
   222  			return MaxAppSpecificPenalty
   223  		}
   224  
   225  		// checks if peer is an access node, and if so, pushes it to the
   226  		// edges of the network by giving the minimum penalty.
   227  		if flowId.Role == flow.RoleAccess {
   228  			lg.Trace().
   229  				Msg("pushing access node to edge by penalizing with minimum penalty value")
   230  			return MinAppSpecificPenalty
   231  		}
   232  
   233  		lg.Trace().
   234  			Msg("rewarding well-behaved non-access node peer with maximum reward value")
   235  		return MaxAppSpecificReward
   236  	}
   237  }