github.com/theQRL/go-zond@v0.2.1/zond/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 zond
    18  
    19  import (
    20  	"errors"
    21  	"sync"
    22  
    23  	"github.com/theQRL/go-zond/common"
    24  	"github.com/theQRL/go-zond/p2p"
    25  	"github.com/theQRL/go-zond/zond/protocols/snap"
    26  	"github.com/theQRL/go-zond/zond/protocols/zond"
    27  )
    28  
    29  var (
    30  	// errPeerSetClosed is returned if a peer is attempted to be added or removed
    31  	// from the peer set after it has been terminated.
    32  	errPeerSetClosed = errors.New("peerset closed")
    33  
    34  	// errPeerAlreadyRegistered is returned if a peer is attempted to be added
    35  	// to the peer set, but one with the same id already exists.
    36  	errPeerAlreadyRegistered = errors.New("peer already registered")
    37  
    38  	// errPeerNotRegistered is returned if a peer is attempted to be removed from
    39  	// a peer set, but no peer with the given id exists.
    40  	errPeerNotRegistered = errors.New("peer not registered")
    41  
    42  	// errSnapWithoutZond is returned if a peer attempts to connect only on the
    43  	// snap protocol without advertising the zond main protocol.
    44  	errSnapWithoutZond = errors.New("peer connected on snap without compatible zond support")
    45  )
    46  
    47  // peerSet represents the collection of active peers currently participating in
    48  // the `zond` protocol, with or without the `snap` extension.
    49  type peerSet struct {
    50  	peers     map[string]*zondPeer // Peers connected on the `zond` protocol
    51  	snapPeers int                  // Number of `snap` compatible peers for connection prioritization
    52  
    53  	snapWait map[string]chan *snap.Peer // Peers connected on `zond` waiting for their snap extension
    54  	snapPend map[string]*snap.Peer      // Peers connected on the `snap` protocol, but not yet on `zond`
    55  
    56  	lock   sync.RWMutex
    57  	closed bool
    58  }
    59  
    60  // newPeerSet creates a new peer set to track the active participants.
    61  func newPeerSet() *peerSet {
    62  	return &peerSet{
    63  		peers:    make(map[string]*zondPeer),
    64  		snapWait: make(map[string]chan *snap.Peer),
    65  		snapPend: make(map[string]*snap.Peer),
    66  	}
    67  }
    68  
    69  // registerSnapExtension unblocks an already connected `zond` peer waiting for its
    70  // `snap` extension, or if no such peer exists, tracks the extension for the time
    71  // being until the `zond` main protocol starts looking for it.
    72  func (ps *peerSet) registerSnapExtension(peer *snap.Peer) error {
    73  	// Reject the peer if it advertises `snap` without `zond` as `snap` is only a
    74  	// satellite protocol meaningful with the chain selection of `zond`
    75  	if !peer.RunningCap(zond.ProtocolName, zond.ProtocolVersions) {
    76  		return errSnapWithoutZond
    77  	}
    78  	// Ensure nobody can double connect
    79  	ps.lock.Lock()
    80  	defer ps.lock.Unlock()
    81  
    82  	id := peer.ID()
    83  	if _, ok := ps.peers[id]; ok {
    84  		return errPeerAlreadyRegistered // avoid connections with the same id as existing ones
    85  	}
    86  	if _, ok := ps.snapPend[id]; ok {
    87  		return errPeerAlreadyRegistered // avoid connections with the same id as pending ones
    88  	}
    89  	// Inject the peer into an `zond` counterpart is available, otherwise save for later
    90  	if wait, ok := ps.snapWait[id]; ok {
    91  		delete(ps.snapWait, id)
    92  		wait <- peer
    93  		return nil
    94  	}
    95  	ps.snapPend[id] = peer
    96  	return nil
    97  }
    98  
    99  // waitSnapExtension blocks until all satellite protocols are connected and tracked
   100  // by the peerset.
   101  func (ps *peerSet) waitSnapExtension(peer *zond.Peer) (*snap.Peer, error) {
   102  	// If the peer does not support a compatible `snap`, don't wait
   103  	if !peer.RunningCap(snap.ProtocolName, snap.ProtocolVersions) {
   104  		return nil, nil
   105  	}
   106  	// Ensure nobody can double connect
   107  	ps.lock.Lock()
   108  
   109  	id := peer.ID()
   110  	if _, ok := ps.peers[id]; ok {
   111  		ps.lock.Unlock()
   112  		return nil, errPeerAlreadyRegistered // avoid connections with the same id as existing ones
   113  	}
   114  	if _, ok := ps.snapWait[id]; ok {
   115  		ps.lock.Unlock()
   116  		return nil, errPeerAlreadyRegistered // avoid connections with the same id as pending ones
   117  	}
   118  	// If `snap` already connected, retrieve the peer from the pending set
   119  	if snap, ok := ps.snapPend[id]; ok {
   120  		delete(ps.snapPend, id)
   121  
   122  		ps.lock.Unlock()
   123  		return snap, nil
   124  	}
   125  	// Otherwise wait for `snap` to connect concurrently
   126  	wait := make(chan *snap.Peer)
   127  	ps.snapWait[id] = wait
   128  	ps.lock.Unlock()
   129  
   130  	return <-wait, nil
   131  }
   132  
   133  // registerPeer injects a new `zond` peer into the working set, or returns an error
   134  // if the peer is already known.
   135  func (ps *peerSet) registerPeer(peer *zond.Peer, ext *snap.Peer) error {
   136  	// Start tracking the new peer
   137  	ps.lock.Lock()
   138  	defer ps.lock.Unlock()
   139  
   140  	if ps.closed {
   141  		return errPeerSetClosed
   142  	}
   143  	id := peer.ID()
   144  	if _, ok := ps.peers[id]; ok {
   145  		return errPeerAlreadyRegistered
   146  	}
   147  	eth := &zondPeer{
   148  		Peer: peer,
   149  	}
   150  	if ext != nil {
   151  		eth.snapExt = &snapPeer{ext}
   152  		ps.snapPeers++
   153  	}
   154  	ps.peers[id] = eth
   155  	return nil
   156  }
   157  
   158  // unregisterPeer removes a remote peer from the active set, disabling any further
   159  // actions to/from that particular entity.
   160  func (ps *peerSet) unregisterPeer(id string) error {
   161  	ps.lock.Lock()
   162  	defer ps.lock.Unlock()
   163  
   164  	peer, ok := ps.peers[id]
   165  	if !ok {
   166  		return errPeerNotRegistered
   167  	}
   168  	delete(ps.peers, id)
   169  	if peer.snapExt != nil {
   170  		ps.snapPeers--
   171  	}
   172  	return nil
   173  }
   174  
   175  // peer retrieves the registered peer with the given id.
   176  func (ps *peerSet) peer(id string) *zondPeer {
   177  	ps.lock.RLock()
   178  	defer ps.lock.RUnlock()
   179  
   180  	return ps.peers[id]
   181  }
   182  
   183  // peersWithoutTransaction retrieves a list of peers that do not have a given
   184  // transaction in their set of known hashes.
   185  func (ps *peerSet) peersWithoutTransaction(hash common.Hash) []*zondPeer {
   186  	ps.lock.RLock()
   187  	defer ps.lock.RUnlock()
   188  
   189  	list := make([]*zondPeer, 0, len(ps.peers))
   190  	for _, p := range ps.peers {
   191  		if !p.KnownTransaction(hash) {
   192  			list = append(list, p)
   193  		}
   194  	}
   195  	return list
   196  }
   197  
   198  // len returns if the current number of `zond` peers in the set. Since the `snap`
   199  // peers are tied to the existence of an `zond` connection, that will always be a
   200  // subset of `zond`.
   201  func (ps *peerSet) len() int {
   202  	ps.lock.RLock()
   203  	defer ps.lock.RUnlock()
   204  
   205  	return len(ps.peers)
   206  }
   207  
   208  // snapLen returns if the current number of `snap` peers in the set.
   209  func (ps *peerSet) snapLen() int {
   210  	ps.lock.RLock()
   211  	defer ps.lock.RUnlock()
   212  
   213  	return ps.snapPeers
   214  }
   215  
   216  // close disconnects all peers.
   217  func (ps *peerSet) close() {
   218  	ps.lock.Lock()
   219  	defer ps.lock.Unlock()
   220  
   221  	for _, p := range ps.peers {
   222  		p.Disconnect(p2p.DiscQuitting)
   223  	}
   224  	ps.closed = true
   225  }