github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/p2p/peers/scorers/bad_responses.go (about) 1 package scorers 2 3 import ( 4 "time" 5 6 "github.com/libp2p/go-libp2p-core/peer" 7 "github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers/peerdata" 8 ) 9 10 var _ Scorer = (*BadResponsesScorer)(nil) 11 12 const ( 13 // DefaultBadResponsesThreshold defines how many bad responses to tolerate before peer is deemed bad. 14 DefaultBadResponsesThreshold = 6 15 // DefaultBadResponsesDecayInterval defines how often to decay previous statistics. 16 // Every interval bad responses counter will be decremented by 1. 17 DefaultBadResponsesDecayInterval = time.Hour 18 ) 19 20 // BadResponsesScorer represents bad responses scoring service. 21 type BadResponsesScorer struct { 22 config *BadResponsesScorerConfig 23 store *peerdata.Store 24 } 25 26 // BadResponsesScorerConfig holds configuration parameters for bad response scoring service. 27 type BadResponsesScorerConfig struct { 28 // Threshold specifies number of bad responses tolerated, before peer is banned. 29 Threshold int 30 // DecayInterval specifies how often bad response stats should be decayed. 31 DecayInterval time.Duration 32 } 33 34 // newBadResponsesScorer creates new bad responses scoring service. 35 func newBadResponsesScorer(store *peerdata.Store, config *BadResponsesScorerConfig) *BadResponsesScorer { 36 if config == nil { 37 config = &BadResponsesScorerConfig{} 38 } 39 scorer := &BadResponsesScorer{ 40 config: config, 41 store: store, 42 } 43 if scorer.config.Threshold == 0 { 44 scorer.config.Threshold = DefaultBadResponsesThreshold 45 } 46 if scorer.config.DecayInterval == 0 { 47 scorer.config.DecayInterval = DefaultBadResponsesDecayInterval 48 } 49 return scorer 50 } 51 52 // Score returns score (penalty) of bad responses peer produced. 53 func (s *BadResponsesScorer) Score(pid peer.ID) float64 { 54 s.store.RLock() 55 defer s.store.RUnlock() 56 return s.score(pid) 57 } 58 59 // score is a lock-free version of Score. 60 func (s *BadResponsesScorer) score(pid peer.ID) float64 { 61 if s.isBadPeer(pid) { 62 return BadPeerScore 63 } 64 score := float64(0) 65 peerData, ok := s.store.PeerData(pid) 66 if !ok { 67 return score 68 } 69 if peerData.BadResponses > 0 { 70 score = float64(peerData.BadResponses) / float64(s.config.Threshold) 71 // Since score represents a penalty, negate it. 72 score *= -1 73 } 74 return score 75 } 76 77 // Params exposes scorer's parameters. 78 func (s *BadResponsesScorer) Params() *BadResponsesScorerConfig { 79 return s.config 80 } 81 82 // Count obtains the number of bad responses we have received from the given remote peer. 83 func (s *BadResponsesScorer) Count(pid peer.ID) (int, error) { 84 s.store.RLock() 85 defer s.store.RUnlock() 86 return s.count(pid) 87 } 88 89 // count is a lock-free version of Count. 90 func (s *BadResponsesScorer) count(pid peer.ID) (int, error) { 91 if peerData, ok := s.store.PeerData(pid); ok { 92 return peerData.BadResponses, nil 93 } 94 return -1, peerdata.ErrPeerUnknown 95 } 96 97 // Increment increments the number of bad responses we have received from the given remote peer. 98 // If peer doesn't exist this method is no-op. 99 func (s *BadResponsesScorer) Increment(pid peer.ID) { 100 s.store.Lock() 101 defer s.store.Unlock() 102 103 peerData, ok := s.store.PeerData(pid) 104 if !ok { 105 s.store.SetPeerData(pid, &peerdata.PeerData{ 106 BadResponses: 1, 107 }) 108 return 109 } 110 peerData.BadResponses++ 111 } 112 113 // IsBadPeer states if the peer is to be considered bad. 114 // If the peer is unknown this will return `false`, which makes using this function easier than returning an error. 115 func (s *BadResponsesScorer) IsBadPeer(pid peer.ID) bool { 116 s.store.RLock() 117 defer s.store.RUnlock() 118 return s.isBadPeer(pid) 119 } 120 121 // isBadPeer is lock-free version of IsBadPeer. 122 func (s *BadResponsesScorer) isBadPeer(pid peer.ID) bool { 123 if peerData, ok := s.store.PeerData(pid); ok { 124 return peerData.BadResponses >= s.config.Threshold 125 } 126 return false 127 } 128 129 // BadPeers returns the peers that are considered bad. 130 func (s *BadResponsesScorer) BadPeers() []peer.ID { 131 s.store.RLock() 132 defer s.store.RUnlock() 133 134 badPeers := make([]peer.ID, 0) 135 for pid := range s.store.Peers() { 136 if s.isBadPeer(pid) { 137 badPeers = append(badPeers, pid) 138 } 139 } 140 return badPeers 141 } 142 143 // Decay reduces the bad responses of all peers, giving reformed peers a chance to join the network. 144 // This can be run periodically, although note that each time it runs it does give all bad peers another chance as well 145 // to clog up the network with bad responses, so should not be run too frequently; once an hour would be reasonable. 146 func (s *BadResponsesScorer) Decay() { 147 s.store.Lock() 148 defer s.store.Unlock() 149 150 for _, peerData := range s.store.Peers() { 151 if peerData.BadResponses > 0 { 152 peerData.BadResponses-- 153 } 154 } 155 }