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  }