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 }