github.com/jimmyx0x/go-ethereum@v1.10.28/eth/peerset.go (about)

     1  // Copyright 2020 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package eth
    18  
    19  import (
    20  	"errors"
    21  	"math/big"
    22  	"sync"
    23  
    24  	"github.com/ethereum/go-ethereum/common"
    25  	"github.com/ethereum/go-ethereum/eth/protocols/eth"
    26  	"github.com/ethereum/go-ethereum/eth/protocols/snap"
    27  	"github.com/ethereum/go-ethereum/p2p"
    28  )
    29  
    30  var (
    31  	// errPeerSetClosed is returned if a peer is attempted to be added or removed
    32  	// from the peer set after it has been terminated.
    33  	errPeerSetClosed = errors.New("peerset closed")
    34  
    35  	// errPeerAlreadyRegistered is returned if a peer is attempted to be added
    36  	// to the peer set, but one with the same id already exists.
    37  	errPeerAlreadyRegistered = errors.New("peer already registered")
    38  
    39  	// errPeerNotRegistered is returned if a peer is attempted to be removed from
    40  	// a peer set, but no peer with the given id exists.
    41  	errPeerNotRegistered = errors.New("peer not registered")
    42  
    43  	// errSnapWithoutEth is returned if a peer attempts to connect only on the
    44  	// snap protocol without advertising the eth main protocol.
    45  	errSnapWithoutEth = errors.New("peer connected on snap without compatible eth support")
    46  )
    47  
    48  // peerSet represents the collection of active peers currently participating in
    49  // the `eth` protocol, with or without the `snap` extension.
    50  type peerSet struct {
    51  	peers     map[string]*ethPeer // Peers connected on the `eth` protocol
    52  	snapPeers int                 // Number of `snap` compatible peers for connection prioritization
    53  
    54  	snapWait map[string]chan *snap.Peer // Peers connected on `eth` waiting for their snap extension
    55  	snapPend map[string]*snap.Peer      // Peers connected on the `snap` protocol, but not yet on `eth`
    56  
    57  	lock   sync.RWMutex
    58  	closed bool
    59  }
    60  
    61  // newPeerSet creates a new peer set to track the active participants.
    62  func newPeerSet() *peerSet {
    63  	return &peerSet{
    64  		peers:    make(map[string]*ethPeer),
    65  		snapWait: make(map[string]chan *snap.Peer),
    66  		snapPend: make(map[string]*snap.Peer),
    67  	}
    68  }
    69  
    70  // registerSnapExtension unblocks an already connected `eth` peer waiting for its
    71  // `snap` extension, or if no such peer exists, tracks the extension for the time
    72  // being until the `eth` main protocol starts looking for it.
    73  func (ps *peerSet) registerSnapExtension(peer *snap.Peer) error {
    74  	// Reject the peer if it advertises `snap` without `eth` as `snap` is only a
    75  	// satellite protocol meaningful with the chain selection of `eth`
    76  	if !peer.RunningCap(eth.ProtocolName, eth.ProtocolVersions) {
    77  		return errSnapWithoutEth
    78  	}
    79  	// Ensure nobody can double connect
    80  	ps.lock.Lock()
    81  	defer ps.lock.Unlock()
    82  
    83  	id := peer.ID()
    84  	if _, ok := ps.peers[id]; ok {
    85  		return errPeerAlreadyRegistered // avoid connections with the same id as existing ones
    86  	}
    87  	if _, ok := ps.snapPend[id]; ok {
    88  		return errPeerAlreadyRegistered // avoid connections with the same id as pending ones
    89  	}
    90  	// Inject the peer into an `eth` counterpart is available, otherwise save for later
    91  	if wait, ok := ps.snapWait[id]; ok {
    92  		delete(ps.snapWait, id)
    93  		wait <- peer
    94  		return nil
    95  	}
    96  	ps.snapPend[id] = peer
    97  	return nil
    98  }
    99  
   100  // waitExtensions blocks until all satellite protocols are connected and tracked
   101  // by the peerset.
   102  func (ps *peerSet) waitSnapExtension(peer *eth.Peer) (*snap.Peer, error) {
   103  	// If the peer does not support a compatible `snap`, don't wait
   104  	if !peer.RunningCap(snap.ProtocolName, snap.ProtocolVersions) {
   105  		return nil, nil
   106  	}
   107  	// Ensure nobody can double connect
   108  	ps.lock.Lock()
   109  
   110  	id := peer.ID()
   111  	if _, ok := ps.peers[id]; ok {
   112  		ps.lock.Unlock()
   113  		return nil, errPeerAlreadyRegistered // avoid connections with the same id as existing ones
   114  	}
   115  	if _, ok := ps.snapWait[id]; ok {
   116  		ps.lock.Unlock()
   117  		return nil, errPeerAlreadyRegistered // avoid connections with the same id as pending ones
   118  	}
   119  	// If `snap` already connected, retrieve the peer from the pending set
   120  	if snap, ok := ps.snapPend[id]; ok {
   121  		delete(ps.snapPend, id)
   122  
   123  		ps.lock.Unlock()
   124  		return snap, nil
   125  	}
   126  	// Otherwise wait for `snap` to connect concurrently
   127  	wait := make(chan *snap.Peer)
   128  	ps.snapWait[id] = wait
   129  	ps.lock.Unlock()
   130  
   131  	return <-wait, nil
   132  }
   133  
   134  // registerPeer injects a new `eth` peer into the working set, or returns an error
   135  // if the peer is already known.
   136  func (ps *peerSet) registerPeer(peer *eth.Peer, ext *snap.Peer) error {
   137  	// Start tracking the new peer
   138  	ps.lock.Lock()
   139  	defer ps.lock.Unlock()
   140  
   141  	if ps.closed {
   142  		return errPeerSetClosed
   143  	}
   144  	id := peer.ID()
   145  	if _, ok := ps.peers[id]; ok {
   146  		return errPeerAlreadyRegistered
   147  	}
   148  	eth := &ethPeer{
   149  		Peer: peer,
   150  	}
   151  	if ext != nil {
   152  		eth.snapExt = &snapPeer{ext}
   153  		ps.snapPeers++
   154  	}
   155  	ps.peers[id] = eth
   156  	return nil
   157  }
   158  
   159  // unregisterPeer removes a remote peer from the active set, disabling any further
   160  // actions to/from that particular entity.
   161  func (ps *peerSet) unregisterPeer(id string) error {
   162  	ps.lock.Lock()
   163  	defer ps.lock.Unlock()
   164  
   165  	peer, ok := ps.peers[id]
   166  	if !ok {
   167  		return errPeerNotRegistered
   168  	}
   169  	delete(ps.peers, id)
   170  	if peer.snapExt != nil {
   171  		ps.snapPeers--
   172  	}
   173  	return nil
   174  }
   175  
   176  // peer retrieves the registered peer with the given id.
   177  func (ps *peerSet) peer(id string) *ethPeer {
   178  	ps.lock.RLock()
   179  	defer ps.lock.RUnlock()
   180  
   181  	return ps.peers[id]
   182  }
   183  
   184  // peersWithoutBlock retrieves a list of peers that do not have a given block in
   185  // their set of known hashes so it might be propagated to them.
   186  func (ps *peerSet) peersWithoutBlock(hash common.Hash) []*ethPeer {
   187  	ps.lock.RLock()
   188  	defer ps.lock.RUnlock()
   189  
   190  	list := make([]*ethPeer, 0, len(ps.peers))
   191  	for _, p := range ps.peers {
   192  		if !p.KnownBlock(hash) {
   193  			list = append(list, p)
   194  		}
   195  	}
   196  	return list
   197  }
   198  
   199  // peersWithoutTransaction retrieves a list of peers that do not have a given
   200  // transaction in their set of known hashes.
   201  func (ps *peerSet) peersWithoutTransaction(hash common.Hash) []*ethPeer {
   202  	ps.lock.RLock()
   203  	defer ps.lock.RUnlock()
   204  
   205  	list := make([]*ethPeer, 0, len(ps.peers))
   206  	for _, p := range ps.peers {
   207  		if !p.KnownTransaction(hash) {
   208  			list = append(list, p)
   209  		}
   210  	}
   211  	return list
   212  }
   213  
   214  // len returns if the current number of `eth` peers in the set. Since the `snap`
   215  // peers are tied to the existence of an `eth` connection, that will always be a
   216  // subset of `eth`.
   217  func (ps *peerSet) len() int {
   218  	ps.lock.RLock()
   219  	defer ps.lock.RUnlock()
   220  
   221  	return len(ps.peers)
   222  }
   223  
   224  // snapLen returns if the current number of `snap` peers in the set.
   225  func (ps *peerSet) snapLen() int {
   226  	ps.lock.RLock()
   227  	defer ps.lock.RUnlock()
   228  
   229  	return ps.snapPeers
   230  }
   231  
   232  // peerWithHighestTD retrieves the known peer with the currently highest total
   233  // difficulty, but below the given PoS switchover threshold.
   234  func (ps *peerSet) peerWithHighestTD() *eth.Peer {
   235  	ps.lock.RLock()
   236  	defer ps.lock.RUnlock()
   237  
   238  	var (
   239  		bestPeer *eth.Peer
   240  		bestTd   *big.Int
   241  	)
   242  	for _, p := range ps.peers {
   243  		if _, td := p.Head(); bestPeer == nil || td.Cmp(bestTd) > 0 {
   244  			bestPeer, bestTd = p.Peer, td
   245  		}
   246  	}
   247  	return bestPeer
   248  }
   249  
   250  // close disconnects all peers.
   251  func (ps *peerSet) close() {
   252  	ps.lock.Lock()
   253  	defer ps.lock.Unlock()
   254  
   255  	for _, p := range ps.peers {
   256  		p.Disconnect(p2p.DiscQuitting)
   257  	}
   258  	ps.closed = true
   259  }