github.com/dominant-strategies/go-quai@v0.28.2/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/dominant-strategies/go-quai/common"
    25  	"github.com/dominant-strategies/go-quai/eth/protocols/eth"
    26  	"github.com/dominant-strategies/go-quai/p2p"
    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  	// errSnapWithoutEth is returned if a peer attempts to connect only on the
    43  	// snap protocol without advertizing the eth main protocol.
    44  	errSnapWithoutEth = errors.New("peer connected on snap without compatible eth support")
    45  )
    46  
    47  // peerSet represents the collection of active peers currently participating in
    48  // the `eth` protocol, with or without the `snap` extension.
    49  type peerSet struct {
    50  	peers  map[string]*ethPeer // Peers connected on the `eth` protocol
    51  	lock   sync.RWMutex
    52  	closed bool
    53  }
    54  
    55  // newPeerSet creates a new peer set to track the active participants.
    56  func newPeerSet() *peerSet {
    57  	return &peerSet{
    58  		peers: make(map[string]*ethPeer),
    59  	}
    60  }
    61  
    62  // registerPeer injects a new `eth` peer into the working set, or returns an error
    63  // if the peer is already known.
    64  func (ps *peerSet) registerPeer(peer *eth.Peer) error {
    65  	// Start tracking the new peer
    66  	ps.lock.Lock()
    67  	defer ps.lock.Unlock()
    68  
    69  	if ps.closed {
    70  		return errPeerSetClosed
    71  	}
    72  	id := peer.ID()
    73  	if _, ok := ps.peers[id]; ok {
    74  		return errPeerAlreadyRegistered
    75  	}
    76  	eth := &ethPeer{
    77  		Peer: peer,
    78  	}
    79  	ps.peers[id] = eth
    80  	return nil
    81  }
    82  
    83  // unregisterPeer removes a remote peer from the active set, disabling any further
    84  // actions to/from that particular entity.
    85  func (ps *peerSet) unregisterPeer(id string) error {
    86  	ps.lock.Lock()
    87  	defer ps.lock.Unlock()
    88  	_, ok := ps.peers[id]
    89  	if !ok {
    90  		return errPeerNotRegistered
    91  	}
    92  	delete(ps.peers, id)
    93  	return nil
    94  }
    95  
    96  // peer retrieves the registered peer with the given id.
    97  func (ps *peerSet) peer(id string) *ethPeer {
    98  	ps.lock.RLock()
    99  	defer ps.lock.RUnlock()
   100  
   101  	return ps.peers[id]
   102  }
   103  
   104  // peersWithoutBlock retrieves a list of peers that do not have a given block in
   105  // their set of known hashes so it might be propagated to them.
   106  func (ps *peerSet) peersWithoutBlock(hash common.Hash) []*ethPeer {
   107  	ps.lock.RLock()
   108  	defer ps.lock.RUnlock()
   109  
   110  	list := make([]*ethPeer, 0, len(ps.peers))
   111  	for _, p := range ps.peers {
   112  		if !p.KnownBlock(hash) {
   113  			list = append(list, p)
   114  		}
   115  	}
   116  	return list
   117  }
   118  
   119  // peersWithoutTransaction retrieves a list of peers that do not have a given
   120  // transaction in their set of known hashes.
   121  func (ps *peerSet) peersWithoutTransaction(hash common.Hash) []*ethPeer {
   122  	ps.lock.RLock()
   123  	defer ps.lock.RUnlock()
   124  
   125  	list := make([]*ethPeer, 0, len(ps.peers))
   126  	for _, p := range ps.peers {
   127  		if !p.KnownTransaction(hash) {
   128  			list = append(list, p)
   129  		}
   130  	}
   131  	return list
   132  }
   133  
   134  // len returns if the current number of `eth` peers in the set. Since the `snap`
   135  // peers are tied to the existence of an `eth` connection, that will always be a
   136  // subset of `eth`.
   137  func (ps *peerSet) len() int {
   138  	ps.lock.RLock()
   139  	defer ps.lock.RUnlock()
   140  
   141  	return len(ps.peers)
   142  }
   143  
   144  // peerWithHighestEntropy retrieves the known peer with the currently highest Entropy
   145  func (ps *peerSet) peerWithHighestEntropy() *eth.Peer {
   146  	ps.lock.RLock()
   147  	defer ps.lock.RUnlock()
   148  
   149  	var (
   150  		bestPeer    *eth.Peer
   151  		bestEntropy *big.Int
   152  	)
   153  	for _, p := range ps.peers {
   154  		if _, _, entropy, _ := p.Head(); bestPeer == nil || entropy.Cmp(bestEntropy) > 0 {
   155  			bestPeer, bestEntropy = p.Peer, entropy
   156  		}
   157  	}
   158  	return bestPeer
   159  }
   160  
   161  func (ps *peerSet) peerRunningSlice(location common.Location) []*eth.Peer {
   162  	ps.lock.RLock()
   163  	defer ps.lock.RUnlock()
   164  
   165  	containsLocation := func(s []common.Location, e common.Location) bool {
   166  		for _, a := range s {
   167  			if common.Location.Equal(a, e) {
   168  				return true
   169  			}
   170  		}
   171  		return false
   172  	}
   173  
   174  	var peersRunningSlice []*eth.Peer
   175  	for _, p := range ps.peers {
   176  		if containsLocation(p.Peer.SlicesRunning(), location) {
   177  			peersRunningSlice = append(peersRunningSlice, p.Peer)
   178  		}
   179  	}
   180  	return peersRunningSlice
   181  }
   182  
   183  func (ps *peerSet) allPeers() []*eth.Peer {
   184  	ps.lock.RLock()
   185  	defer ps.lock.RUnlock()
   186  
   187  	var allPeers []*eth.Peer
   188  	for _, p := range ps.peers {
   189  		allPeers = append(allPeers, p.Peer)
   190  	}
   191  	return allPeers
   192  }
   193  
   194  // close disconnects all peers.
   195  func (ps *peerSet) close() {
   196  	ps.lock.Lock()
   197  	defer ps.lock.Unlock()
   198  
   199  	for _, p := range ps.peers {
   200  		p.Disconnect(p2p.DiscQuitting)
   201  	}
   202  	ps.closed = true
   203  }