github.com/ethereum-optimism/optimism@v1.7.2/op-node/p2p/peer_scorer.go (about) 1 package p2p 2 3 import ( 4 "time" 5 6 "github.com/ethereum-optimism/optimism/op-node/rollup" 7 8 "github.com/ethereum-optimism/optimism/op-node/p2p/store" 9 log "github.com/ethereum/go-ethereum/log" 10 pubsub "github.com/libp2p/go-libp2p-pubsub" 11 peer "github.com/libp2p/go-libp2p/core/peer" 12 ) 13 14 type scorer struct { 15 peerStore Peerstore 16 metricer ScoreMetrics 17 appScorer ApplicationScorer 18 log log.Logger 19 cfg *rollup.Config 20 } 21 22 // Peerstore is a subset of the libp2p peerstore.Peerstore interface. 23 // 24 //go:generate mockery --name Peerstore --output mocks/ 25 type Peerstore interface { 26 // PeerInfo returns a peer.PeerInfo struct for given peer.ID. 27 // This is a small slice of the information Peerstore has on 28 // that peer, useful to other services. 29 PeerInfo(peer.ID) peer.AddrInfo 30 31 // Peers returns all of the peer IDs stored across all inner stores. 32 Peers() peer.IDSlice 33 34 SetScore(id peer.ID, diff store.ScoreDiff) (store.PeerScores, error) 35 } 36 37 // Scorer is a peer scorer that scores peers based on application-specific metrics. 38 type Scorer interface { 39 SnapshotHook() pubsub.ExtendedPeerScoreInspectFn 40 ApplicationScore(peer.ID) float64 41 } 42 43 //go:generate mockery --name ScoreMetrics --output mocks/ 44 type ScoreMetrics interface { 45 SetPeerScores([]store.PeerScores) 46 } 47 48 // NewScorer returns a new peer scorer. 49 func NewScorer(cfg *rollup.Config, peerStore Peerstore, metricer ScoreMetrics, appScorer ApplicationScorer, log log.Logger) Scorer { 50 return &scorer{ 51 peerStore: peerStore, 52 metricer: metricer, 53 appScorer: appScorer, 54 log: log, 55 cfg: cfg, 56 } 57 } 58 59 // SnapshotHook returns a function that is called periodically by the pubsub library to inspect the gossip peer scores. 60 // It is passed into the pubsub library as a [pubsub.ExtendedPeerScoreInspectFn] in the [pubsub.WithPeerScoreInspect] option. 61 // The returned [pubsub.ExtendedPeerScoreInspectFn] is called with a mapping of peer IDs to peer score snapshots. 62 // The incoming peer score snapshots only contain gossip-score components. 63 func (s *scorer) SnapshotHook() pubsub.ExtendedPeerScoreInspectFn { 64 blocksTopicName := blocksTopicV1(s.cfg) 65 return func(m map[peer.ID]*pubsub.PeerScoreSnapshot) { 66 allScores := make([]store.PeerScores, 0, len(m)) 67 // Now set the new scores. 68 for id, snap := range m { 69 diff := store.GossipScores{ 70 Total: snap.Score, 71 Blocks: store.TopicScores{}, 72 IPColocationFactor: snap.IPColocationFactor, 73 BehavioralPenalty: snap.BehaviourPenalty, 74 } 75 if topSnap, ok := snap.Topics[blocksTopicName]; ok { 76 diff.Blocks.TimeInMesh = float64(topSnap.TimeInMesh) / float64(time.Second) 77 diff.Blocks.MeshMessageDeliveries = topSnap.MeshMessageDeliveries 78 diff.Blocks.FirstMessageDeliveries = topSnap.FirstMessageDeliveries 79 diff.Blocks.InvalidMessageDeliveries = topSnap.InvalidMessageDeliveries 80 } 81 if peerScores, err := s.peerStore.SetScore(id, &diff); err != nil { 82 s.log.Warn("Unable to update peer gossip score", "err", err) 83 } else { 84 allScores = append(allScores, peerScores) 85 } 86 } 87 s.metricer.SetPeerScores(allScores) 88 } 89 } 90 91 func (s *scorer) ApplicationScore(id peer.ID) float64 { 92 return s.appScorer.ApplicationScore(id) 93 }