func (m *PeerManager) DialFailed(address NodeAddress) error {
	m.mtx.Lock()
	defer m.mtx.Unlock()
	delete(m.dialing, address.NodeID)
	for from, to := range m.upgrading {
		if to == address.NodeID {
			delete(m.upgrading, from)  
		}
	}
	peer, ok := m.store.Get(address.NodeID)
	if !ok {  
		return nil
	}
	addressInfo, ok := peer.AddressInfo[address]
	if !ok {
		return nil  
	}
	addressInfo.LastDialFailure = time.Now().UTC()
	addressInfo.DialFailures++
	if err := m.store.Set(peer); err != nil {
		return err
	}
	if d := m.retryDelay(addressInfo.DialFailures, peer.Persistent); d != 0 && d != retryNever {
		go func() {
			timer := time.NewTimer(d)
			defer timer.Stop()
			select {
			case <-timer.C:
				m.dialWaker.Wake()
			case <-m.closeCh:
			}
		}()
	} else {
		m.dialWaker.Wake()
	}
	return nil
}
DialFailed reports a failed dial attempt. This will make the peer available
for dialing again when appropriate (possibly after a retry timeout).

FIXME: This should probably delete or mark bad addresses/peers after some time.
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
}
func (m *PeerManager) configurePeer(peer peerInfo) peerInfo {
	peer.Persistent = m.options.isPersistent(peer.ID)
	peer.FixedScore = m.options.PeerScores[peer.ID]
	return peer
}
func (m *PeerManager) newPeerInfo(id types.NodeID) peerInfo {
	peerInfo := peerInfo{
		ID:          id,
		AddressInfo: map[NodeAddress]*peerAddressInfo{},
	}
	return m.configurePeer(peerInfo)
}
func (m *PeerManager) prunePeers() error {
	if m.options.MaxPeers == 0 || m.store.Size() <= int(m.options.MaxPeers) {
		return nil
	}
	ranked := m.store.Ranked()
	for i := len(ranked) - 1; i >= 0; i-- {
		peerID := ranked[i].ID
		switch {
		case m.store.Size() <= int(m.options.MaxPeers):
			return nil
		case m.dialing[peerID]:
		case m.connected[peerID]:
		default:
			if err := m.store.Delete(peerID); err != nil {
				return err
			}
		}
	}
	return nil
}
func (m *PeerManager) Add(address NodeAddress) (bool, error) {
	if err := address.Validate(); err != nil {
		return false, err
	}
	if address.NodeID == m.selfID {
		return false, fmt.Errorf("can't add self (%v) to peer store", m.selfID)
	}
	m.mtx.Lock()
	defer m.mtx.Unlock()
	peer, ok := m.store.Get(address.NodeID)
	if !ok {
		peer = m.newPeerInfo(address.NodeID)
	}
	_, ok = peer.AddressInfo[address]
	if ok {
		return false, nil
	}
	peer.AddressInfo[address] = &peerAddressInfo{Address: address}
	if err := m.store.Set(peer); err != nil {
		return false, err
	}
	if err := m.prunePeers(); err != nil {
		return true, err
	}
	m.dialWaker.Wake()
	return true, nil
}
func (m *PeerManager) PeerRatio() float64 {
	m.mtx.Lock()
	defer m.mtx.Unlock()
	if m.options.MaxPeers == 0 {
		return 0
	}
	return float64(m.store.Size()) / float64(m.options.MaxPeers)
}
func (m *PeerManager) DialNext(ctx context.Context) (NodeAddress, error) {
	for {
		address, err := m.TryDialNext()
		if err != nil || (address != NodeAddress{}) {
			return address, err
		}
		select {
		case <-m.dialWaker.Sleep():
		case <-ctx.Done():
			return NodeAddress{}, ctx.Err()
		}
	}
}
func (m *PeerManager) TryDialNext() (NodeAddress, error) {
	m.mtx.Lock()
	defer m.mtx.Unlock()
	if m.options.MaxConnected > 0 && len(m.connected)+len(m.dialing) >=
		int(m.options.MaxConnected)+int(m.options.MaxConnectedUpgrade) {
		return NodeAddress{}, nil
	}
	for _, peer := range m.store.Ranked() {
		if m.dialing[peer.ID] || m.connected[peer.ID] {
			continue
		}
		for _, addressInfo := range peer.AddressInfo {
			if time.Since(addressInfo.LastDialFailure) < m.retryDelay(addressInfo.DialFailures, peer.Persistent) {
				continue
			}
			if m.options.MaxConnected > 0 && len(m.connected) >= int(m.options.MaxConnected) {
				upgradeFromPeer := m.findUpgradeCandidate(peer.ID, peer.Score())
				if upgradeFromPeer == "" {
					return NodeAddress{}, nil
				}
				m.upgrading[upgradeFromPeer] = peer.ID
			}
			m.dialing[peer.ID] = true
			return addressInfo.Address, nil
		}
	}
	return NodeAddress{}, nil
}