github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/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  	"fmt"
    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  	quitCh chan struct{} // Quit channel to signal termination
    60  }
    61  
    62  // newPeerSet creates a new peer set to track the active participants.
    63  func newPeerSet() *peerSet {
    64  	return &peerSet{
    65  		peers:    make(map[string]*ethPeer),
    66  		snapWait: make(map[string]chan *snap.Peer),
    67  		snapPend: make(map[string]*snap.Peer),
    68  		quitCh:   make(chan struct{}),
    69  	}
    70  }
    71  
    72  // registerSnapExtension unblocks an already connected `eth` peer waiting for its
    73  // `snap` extension, or if no such peer exists, tracks the extension for the time
    74  // being until the `eth` main protocol starts looking for it.
    75  func (ps *peerSet) registerSnapExtension(peer *snap.Peer) error {
    76  	// Reject the peer if it advertises `snap` without `eth` as `snap` is only a
    77  	// satellite protocol meaningful with the chain selection of `eth`
    78  	if !peer.RunningCap(eth.ProtocolName, eth.ProtocolVersions) {
    79  		return fmt.Errorf("%w: have %v", errSnapWithoutEth, peer.Caps())
    80  	}
    81  	// Ensure nobody can double connect
    82  	ps.lock.Lock()
    83  	defer ps.lock.Unlock()
    84  
    85  	id := peer.ID()
    86  	if _, ok := ps.peers[id]; ok {
    87  		return errPeerAlreadyRegistered // avoid connections with the same id as existing ones
    88  	}
    89  	if _, ok := ps.snapPend[id]; ok {
    90  		return errPeerAlreadyRegistered // avoid connections with the same id as pending ones
    91  	}
    92  	// Inject the peer into an `eth` counterpart is available, otherwise save for later
    93  	if wait, ok := ps.snapWait[id]; ok {
    94  		delete(ps.snapWait, id)
    95  		wait <- peer
    96  		return nil
    97  	}
    98  	ps.snapPend[id] = peer
    99  	return nil
   100  }
   101  
   102  // waitSnapExtension blocks until all satellite protocols are connected and tracked
   103  // by the peerset.
   104  func (ps *peerSet) waitSnapExtension(peer *eth.Peer) (*snap.Peer, error) {
   105  	// If the peer does not support a compatible `snap`, don't wait
   106  	if !peer.RunningCap(snap.ProtocolName, snap.ProtocolVersions) {
   107  		return nil, nil
   108  	}
   109  	// Ensure nobody can double connect
   110  	ps.lock.Lock()
   111  
   112  	id := peer.ID()
   113  	if _, ok := ps.peers[id]; ok {
   114  		ps.lock.Unlock()
   115  		return nil, errPeerAlreadyRegistered // avoid connections with the same id as existing ones
   116  	}
   117  	if _, ok := ps.snapWait[id]; ok {
   118  		ps.lock.Unlock()
   119  		return nil, errPeerAlreadyRegistered // avoid connections with the same id as pending ones
   120  	}
   121  	// If `snap` already connected, retrieve the peer from the pending set
   122  	if snap, ok := ps.snapPend[id]; ok {
   123  		delete(ps.snapPend, id)
   124  
   125  		ps.lock.Unlock()
   126  		return snap, nil
   127  	}
   128  	// Otherwise wait for `snap` to connect concurrently
   129  	wait := make(chan *snap.Peer)
   130  	ps.snapWait[id] = wait
   131  	ps.lock.Unlock()
   132  
   133  	select {
   134  	case p := <-wait:
   135  		return p, nil
   136  	case <-ps.quitCh:
   137  		ps.lock.Lock()
   138  		delete(ps.snapWait, id)
   139  		ps.lock.Unlock()
   140  		return nil, errPeerSetClosed
   141  	}
   142  }
   143  
   144  // registerPeer injects a new `eth` peer into the working set, or returns an error
   145  // if the peer is already known.
   146  func (ps *peerSet) registerPeer(peer *eth.Peer, ext *snap.Peer) error {
   147  	// Start tracking the new peer
   148  	ps.lock.Lock()
   149  	defer ps.lock.Unlock()
   150  
   151  	if ps.closed {
   152  		return errPeerSetClosed
   153  	}
   154  	id := peer.ID()
   155  	if _, ok := ps.peers[id]; ok {
   156  		return errPeerAlreadyRegistered
   157  	}
   158  	eth := &ethPeer{
   159  		Peer: peer,
   160  	}
   161  	if ext != nil {
   162  		eth.snapExt = &snapPeer{ext}
   163  		ps.snapPeers++
   164  	}
   165  	ps.peers[id] = eth
   166  	return nil
   167  }
   168  
   169  // unregisterPeer removes a remote peer from the active set, disabling any further
   170  // actions to/from that particular entity.
   171  func (ps *peerSet) unregisterPeer(id string) error {
   172  	ps.lock.Lock()
   173  	defer ps.lock.Unlock()
   174  
   175  	peer, ok := ps.peers[id]
   176  	if !ok {
   177  		return errPeerNotRegistered
   178  	}
   179  	delete(ps.peers, id)
   180  	if peer.snapExt != nil {
   181  		ps.snapPeers--
   182  	}
   183  	return nil
   184  }
   185  
   186  // peer retrieves the registered peer with the given id.
   187  func (ps *peerSet) peer(id string) *ethPeer {
   188  	ps.lock.RLock()
   189  	defer ps.lock.RUnlock()
   190  
   191  	return ps.peers[id]
   192  }
   193  
   194  // peersWithoutTransaction retrieves a list of peers that do not have a given
   195  // transaction in their set of known hashes.
   196  func (ps *peerSet) peersWithoutTransaction(hash common.Hash) []*ethPeer {
   197  	ps.lock.RLock()
   198  	defer ps.lock.RUnlock()
   199  
   200  	list := make([]*ethPeer, 0, len(ps.peers))
   201  	for _, p := range ps.peers {
   202  		if !p.KnownTransaction(hash) {
   203  			list = append(list, p)
   204  		}
   205  	}
   206  	return list
   207  }
   208  
   209  // len returns if the current number of `eth` peers in the set. Since the `snap`
   210  // peers are tied to the existence of an `eth` connection, that will always be a
   211  // subset of `eth`.
   212  func (ps *peerSet) len() int {
   213  	ps.lock.RLock()
   214  	defer ps.lock.RUnlock()
   215  
   216  	return len(ps.peers)
   217  }
   218  
   219  // snapLen returns if the current number of `snap` peers in the set.
   220  func (ps *peerSet) snapLen() int {
   221  	ps.lock.RLock()
   222  	defer ps.lock.RUnlock()
   223  
   224  	return ps.snapPeers
   225  }
   226  
   227  // close disconnects all peers.
   228  func (ps *peerSet) close() {
   229  	ps.lock.Lock()
   230  	defer ps.lock.Unlock()
   231  
   232  	for _, p := range ps.peers {
   233  		p.Disconnect(p2p.DiscQuitting)
   234  	}
   235  	if !ps.closed {
   236  		close(ps.quitCh)
   237  	}
   238  	ps.closed = true
   239  }