github.com/amazechain/amc@v0.1.3/internal/p2p/peers/scorers/peer_status.go (about) 1 package scorers 2 3 import ( 4 "errors" 5 "github.com/amazechain/amc/api/protocol/sync_pb" 6 "github.com/amazechain/amc/internal/p2p/peers/peerdata" 7 "github.com/amazechain/amc/internal/p2p/types" 8 "github.com/amazechain/amc/utils" 9 "github.com/holiman/uint256" 10 "math" 11 "time" 12 13 "github.com/libp2p/go-libp2p/core/peer" 14 ) 15 16 var _ Scorer = (*PeerStatusScorer)(nil) 17 18 // PeerStatusScorer represents scorer that evaluates peers based on their statuses. 19 // Peer statuses are updated by regularly polling peers (see sync/rpc_status.go). 20 type PeerStatusScorer struct { 21 config *PeerStatusScorerConfig 22 store *peerdata.Store 23 //todo 24 ourCurrentHeight *uint256.Int 25 highestPeerHeadSlot *uint256.Int 26 } 27 28 // PeerStatusScorerConfig holds configuration parameters for peer status scoring service. 29 type PeerStatusScorerConfig struct{} 30 31 // newPeerStatusScorer creates new peer status scoring service. 32 func newPeerStatusScorer(store *peerdata.Store, config *PeerStatusScorerConfig) *PeerStatusScorer { 33 if config == nil { 34 config = &PeerStatusScorerConfig{} 35 } 36 return &PeerStatusScorer{ 37 config: config, 38 store: store, 39 } 40 } 41 42 // Score returns calculated peer score. 43 func (s *PeerStatusScorer) Score(pid peer.ID) float64 { 44 s.store.RLock() 45 defer s.store.RUnlock() 46 return s.score(pid) 47 } 48 49 // score is a lock-free version of Score. 50 func (s *PeerStatusScorer) score(pid peer.ID) float64 { 51 if s.isBadPeer(pid) { 52 return BadPeerScore 53 } 54 score := float64(0) 55 peerData, ok := s.store.PeerData(pid) 56 if !ok || peerData.ChainState == nil { 57 return score 58 } 59 // todo 60 if s.ourCurrentHeight != nil && peerData.CurrentHeight().Cmp(s.ourCurrentHeight) == -1 { 61 return score 62 } 63 // Calculate score as a ratio to the known maximum head slot. 64 // The closer the current peer's head slot to the maximum, the higher is the calculated score. 65 if s.highestPeerHeadSlot.Uint64() > 0 { 66 score = float64(peerData.CurrentHeight().Uint64()) / float64(s.highestPeerHeadSlot.Uint64()) 67 return math.Round(score*ScoreRoundingFactor) / ScoreRoundingFactor 68 } 69 return score 70 } 71 72 // IsBadPeer states if the peer is to be considered bad. 73 func (s *PeerStatusScorer) IsBadPeer(pid peer.ID) bool { 74 s.store.RLock() 75 defer s.store.RUnlock() 76 return s.isBadPeer(pid) 77 } 78 79 // isBadPeer is lock-free version of IsBadPeer. 80 func (s *PeerStatusScorer) isBadPeer(pid peer.ID) bool { 81 peerData, ok := s.store.PeerData(pid) 82 if !ok { 83 return false 84 } 85 // Mark peer as bad, if the latest error is one of the terminal ones. 86 terminalErrs := []error{ 87 p2ptypes.ErrWrongForkDigestVersion, 88 p2ptypes.ErrInvalidFinalizedRoot, 89 p2ptypes.ErrInvalidRequest, 90 } 91 for _, err := range terminalErrs { 92 if errors.Is(peerData.ChainStateValidationError, err) { 93 return true 94 } 95 } 96 return false 97 } 98 99 // BadPeers returns the peers that are considered bad. 100 func (s *PeerStatusScorer) BadPeers() []peer.ID { 101 s.store.RLock() 102 defer s.store.RUnlock() 103 104 badPeers := make([]peer.ID, 0) 105 for pid := range s.store.Peers() { 106 if s.isBadPeer(pid) { 107 badPeers = append(badPeers, pid) 108 } 109 } 110 return badPeers 111 } 112 113 // SetPeerStatus sets chain state data for a given peer. 114 func (s *PeerStatusScorer) SetPeerStatus(pid peer.ID, chainState *sync_pb.Status, validationError error) { 115 s.store.Lock() 116 defer s.store.Unlock() 117 118 peerData := s.store.PeerDataGetOrCreate(pid) 119 peerData.ChainState = chainState 120 peerData.ChainStateLastUpdated = time.Now() 121 peerData.ChainStateValidationError = validationError 122 123 // Update maximum known head slot (scores will be calculated with respect to that maximum value). 124 //todo 125 currentHeight := utils.ConvertH256ToUint256Int(chainState.CurrentHeight) 126 if chainState != nil && s.highestPeerHeadSlot == nil || currentHeight.Cmp(s.highestPeerHeadSlot) == 1 { 127 s.highestPeerHeadSlot = currentHeight 128 } 129 } 130 131 // PeerStatus gets the chain state of the given remote peer. 132 // This can return nil if there is no known chain state for the peer. 133 // This will error if the peer does not exist. 134 func (s *PeerStatusScorer) PeerStatus(pid peer.ID) (*sync_pb.Status, error) { 135 s.store.RLock() 136 defer s.store.RUnlock() 137 return s.peerStatus(pid) 138 } 139 140 // peerStatus lock-free version of PeerStatus. 141 func (s *PeerStatusScorer) peerStatus(pid peer.ID) (*sync_pb.Status, error) { 142 if peerData, ok := s.store.PeerData(pid); ok { 143 if peerData.ChainState == nil { 144 return nil, peerdata.ErrNoPeerStatus 145 } 146 return peerData.ChainState, nil 147 } 148 return nil, peerdata.ErrPeerUnknown 149 } 150 151 // SetCurrentHeight updates known head slot. todo 152 func (s *PeerStatusScorer) SetCurrentHeight(currentHeight *uint256.Int) { 153 s.store.Lock() 154 defer s.store.Unlock() 155 s.ourCurrentHeight = currentHeight 156 }