func (m *PeerManager) configurePeer(peer peerInfo) peerInfo {
	peer.Persistent = m.options.isPersistent(peer.ID)
	peer.FixedScore = m.options.PeerScores[peer.ID]
	return peer
}
configurePeer configures a peer with ephemeral runtime configuration.
package p2p
import (
	"context"
	"errors"
	"fmt"
	"math"
	"math/rand"
	"sort"
	"sync"
	"time"
	"github.com/gogo/protobuf/proto"
	"github.com/google/orderedcode"
	dbm "github.com/tendermint/tm-db"
	tmsync "github.com/tendermint/tendermint/internal/libs/sync"
	p2pproto "github.com/tendermint/tendermint/proto/tendermint/p2p"
	"github.com/tendermint/tendermint/types"
)
const (
	retryNever time.Duration = math.MaxInt64
)
type PeerStatus string
const (
	PeerStatusUp   PeerStatus = "up"    
	PeerStatusDown PeerStatus = "down"  
	PeerStatusGood PeerStatus = "good"  
	PeerStatusBad  PeerStatus = "bad"   
)
type PeerScore uint8
const (
	PeerScorePersistent PeerScore = math.MaxUint8  
)
type PeerUpdate struct {
	NodeID types.NodeID
	Status PeerStatus
}
type PeerUpdates struct {
	routerUpdatesCh  chan PeerUpdate
	reactorUpdatesCh chan PeerUpdate
	closeCh          chan struct{}
	closeOnce        sync.Once
}
func NewPeerUpdates(updatesCh chan PeerUpdate, buf int) *PeerUpdates {
	return &PeerUpdates{
		reactorUpdatesCh: updatesCh,
		routerUpdatesCh:  make(chan PeerUpdate, buf),
		closeCh:          make(chan struct{}),
	}
}
func (pu *PeerUpdates) Updates() <-chan PeerUpdate {
	return pu.reactorUpdatesCh
}
func (pu *PeerUpdates) SendUpdate(update PeerUpdate) {
	select {
	case <-pu.closeCh:
	case pu.routerUpdatesCh <- update:
	}
}
func (pu *PeerUpdates) Close() {
	pu.closeOnce.Do(func() {
		close(pu.closeCh)
	})
}
func (pu *PeerUpdates) Done() <-chan struct{} {
	return pu.closeCh
}
type PeerManagerOptions struct {
	PersistentPeers []types.NodeID
	MaxPeers uint16
	MaxConnected uint16
	MaxConnectedUpgrade uint16
	MinRetryTime time.Duration
	MaxRetryTime time.Duration
	MaxRetryTimePersistent time.Duration
	RetryTimeJitter time.Duration
	PeerScores map[types.NodeID]PeerScore
	PrivatePeers map[types.NodeID]struct{}
	persistentPeers map[types.NodeID]bool
}
func (o *PeerManagerOptions) Validate() error {
	for _, id := range o.PersistentPeers {
		if err := id.Validate(); err != nil {
			return fmt.Errorf("invalid PersistentPeer ID %q: %w", id, err)
		}
	}
	for id := range o.PrivatePeers {
		if err := id.Validate(); err != nil {
			return fmt.Errorf("invalid private peer ID %q: %w", id, err)
		}
	}
	if o.MaxConnected > 0 && len(o.PersistentPeers) > int(o.MaxConnected) {
		return fmt.Errorf("number of persistent peers %v can't exceed MaxConnected %v",
			len(o.PersistentPeers), o.MaxConnected)
	}
	if o.MaxPeers > 0 {
		if o.MaxConnected == 0 || o.MaxConnected+o.MaxConnectedUpgrade > o.MaxPeers {
			return fmt.Errorf("MaxConnected %v and MaxConnectedUpgrade %v can't exceed MaxPeers %v",
				o.MaxConnected, o.MaxConnectedUpgrade, o.MaxPeers)
		}
	}
	if o.MaxRetryTime > 0 {
		if o.MinRetryTime == 0 {
			return errors.New("can't set MaxRetryTime without MinRetryTime")
		}
		if o.MinRetryTime > o.MaxRetryTime {
			return fmt.Errorf("MinRetryTime %v is greater than MaxRetryTime %v",
				o.MinRetryTime, o.MaxRetryTime)
		}
	}
	if o.MaxRetryTimePersistent > 0 {
		if o.MinRetryTime == 0 {
			return errors.New("can't set MaxRetryTimePersistent without MinRetryTime")
		}
		if o.MinRetryTime > o.MaxRetryTimePersistent {
			return fmt.Errorf("MinRetryTime %v is greater than MaxRetryTimePersistent %v",
				o.MinRetryTime, o.MaxRetryTimePersistent)
		}
	}
	return nil
}
func (o *PeerManagerOptions) isPersistent(id types.NodeID) bool {
	if o.persistentPeers == nil {
		panic("isPersistentPeer() called before optimize()")
	}
	return o.persistentPeers[id]
}
func (o *PeerManagerOptions) optimize() {
	o.persistentPeers = make(map[types.NodeID]bool, len(o.PersistentPeers))
	for _, p := range o.PersistentPeers {
		o.persistentPeers[p] = true
	}
}
type PeerManager struct {
	selfID     types.NodeID
	options    PeerManagerOptions
	rand       *rand.Rand
	dialWaker  *tmsync.Waker  
	evictWaker *tmsync.Waker  
	closeCh    chan struct{}  
	closeOnce  sync.Once
	mtx           sync.Mutex
	store         *peerStore
	subscriptions map[*PeerUpdates]*PeerUpdates  
	dialing       map[types.NodeID]bool          
	upgrading     map[types.NodeID]types.NodeID  
	connected     map[types.NodeID]bool          
	ready         map[types.NodeID]bool          
	evict         map[types.NodeID]bool          
	evicting      map[types.NodeID]bool          
}
func NewPeerManager(selfID types.NodeID, peerDB dbm.DB, options PeerManagerOptions) (*PeerManager, error) {
	if selfID == "" {
		return nil, errors.New("self ID not given")
	}
	if err := options.Validate(); err != nil {
		return nil, err
	}
	options.optimize()
	store, err := newPeerStore(peerDB)
	if err != nil {
		return nil, err
	}
	peerManager := &PeerManager{
		selfID:     selfID,
		options:    options,
		rand:       rand.New(rand.NewSource(time.Now().UnixNano())),  
		dialWaker:  tmsync.NewWaker(),
		evictWaker: tmsync.NewWaker(),
		closeCh:    make(chan struct{}),
		store:         store,
		dialing:       map[types.NodeID]bool{},
		upgrading:     map[types.NodeID]types.NodeID{},
		connected:     map[types.NodeID]bool{},
		ready:         map[types.NodeID]bool{},
		evict:         map[types.NodeID]bool{},
		evicting:      map[types.NodeID]bool{},
		subscriptions: map[*PeerUpdates]*PeerUpdates{},
	}
	if err = peerManager.configurePeers(); err != nil {
		return nil, err
	}
	if err = peerManager.prunePeers(); err != nil {
		return nil, err
	}
	return peerManager, nil
}
func (m *PeerManager) configurePeers() error {
	if err := m.store.Delete(m.selfID); err != nil {
		return err
	}
	configure := map[types.NodeID]bool{}
	for _, id := range m.options.PersistentPeers {
		configure[id] = true
	}
	for id := range m.options.PeerScores {
		configure[id] = true
	}
	for id := range configure {
		if peer, ok := m.store.Get(id); ok {
			if err := m.store.Set(m.configurePeer(peer)); err != nil {
				return err
			}
		}
	}
	return nil
}