github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/gossip/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 gossip
    18  
    19  import (
    20  	"errors"
    21  	"sync"
    22  
    23  	"github.com/unicornultrafoundation/go-helios/hash"
    24  	"github.com/unicornultrafoundation/go-u2u/common"
    25  	"github.com/unicornultrafoundation/go-u2u/eth/protocols/snap"
    26  	"github.com/unicornultrafoundation/go-u2u/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  	// errSnapWithoutU2U is returned if a peer attempts to connect only on the
    43  	// snap protocol without advertizing the u2u main protocol.
    44  	errSnapWithoutU2U = errors.New("peer connected on snap without compatible u2u 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]*peer // Peers connected on the `eth` protocol
    51  	snapPeers int              // Number of `snap` compatible peers for connection prioritization
    52  
    53  	snapWait map[string]chan *snap.Peer // Peers connected on `eth` waiting for their snap extension
    54  	snapPend map[string]*snap.Peer      // Peers connected on the `snap` protocol, but not yet on `eth`
    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]*peer),
    64  		snapWait: make(map[string]chan *snap.Peer),
    65  		snapPend: make(map[string]*snap.Peer),
    66  	}
    67  }
    68  
    69  // RegisterSnapExtension unblocks an already connected `eth` peer waiting for its
    70  // `snap` extension, or if no such peer exists, tracks the extension for the time
    71  // being until the `eth` main protocol starts looking for it.
    72  func (ps *peerSet) RegisterSnapExtension(peer *snap.Peer) error {
    73  	// Reject the peer if it is not eligible for a snap protocol
    74  	if !eligibleForSnap(peer.Peer) {
    75  		return errSnapWithoutU2U
    76  	}
    77  	// Ensure nobody can double connect
    78  	ps.lock.Lock()
    79  	defer ps.lock.Unlock()
    80  
    81  	id := peer.ID()
    82  	if _, ok := ps.peers[id]; ok {
    83  		return errPeerAlreadyRegistered // avoid connections with the same id as existing ones
    84  	}
    85  	if _, ok := ps.snapPend[id]; ok {
    86  		return errPeerAlreadyRegistered // avoid connections with the same id as pending ones
    87  	}
    88  	// Inject the peer into an `eth` counterpart is available, otherwise save for later
    89  	if wait, ok := ps.snapWait[id]; ok {
    90  		delete(ps.snapWait, id)
    91  		wait <- peer
    92  		return nil
    93  	}
    94  	ps.snapPend[id] = peer
    95  	return nil
    96  }
    97  
    98  // WaitSnapExtension blocks until all satellite protocols are connected and tracked
    99  // by the peerset.
   100  func (ps *peerSet) WaitSnapExtension(p *peer) (*snap.Peer, error) {
   101  	// If the peer is not eligible for a snap protocol`, don't wait
   102  	if !eligibleForSnap(p.Peer) {
   103  		return nil, nil
   104  	}
   105  	// Ensure nobody can double connect
   106  	ps.lock.Lock()
   107  
   108  	id := p.id
   109  	if _, ok := ps.peers[id]; ok {
   110  		ps.lock.Unlock()
   111  		return nil, errPeerAlreadyRegistered // avoid connections with the same id as existing ones
   112  	}
   113  	if _, ok := ps.snapWait[id]; ok {
   114  		ps.lock.Unlock()
   115  		return nil, errPeerAlreadyRegistered // avoid connections with the same id as pending ones
   116  	}
   117  	// If `snap` already connected, retrieve the peer from the pending set
   118  	if snap, ok := ps.snapPend[id]; ok {
   119  		delete(ps.snapPend, id)
   120  
   121  		ps.lock.Unlock()
   122  		return snap, nil
   123  	}
   124  	// Otherwise wait for `snap` to connect concurrently
   125  	wait := make(chan *snap.Peer)
   126  	ps.snapWait[id] = wait
   127  	ps.lock.Unlock()
   128  
   129  	return <-wait, nil
   130  }
   131  
   132  // RegisterPeer injects a new `eth` peer into the working set, or returns an error
   133  // if the peer is already known.
   134  func (ps *peerSet) RegisterPeer(p *peer, ext *snap.Peer) error {
   135  	// Start tracking the new peer
   136  	ps.lock.Lock()
   137  	defer ps.lock.Unlock()
   138  
   139  	if ps.closed {
   140  		return errPeerSetClosed
   141  	}
   142  
   143  	id := p.id
   144  	if _, ok := ps.peers[id]; ok {
   145  		return errPeerAlreadyRegistered
   146  	}
   147  
   148  	if ext != nil {
   149  		p.snapExt = &snapPeer{ext}
   150  		ps.snapPeers++
   151  	}
   152  
   153  	ps.peers[id] = p
   154  	return nil
   155  }
   156  
   157  // UnregisterPeer removes a remote peer from the active set, disabling any further
   158  // actions to/from that particular entity.
   159  func (ps *peerSet) UnregisterPeer(id string) error {
   160  	ps.lock.Lock()
   161  	defer ps.lock.Unlock()
   162  
   163  	peer, ok := ps.peers[id]
   164  	if !ok {
   165  		return errPeerNotRegistered
   166  	}
   167  	delete(ps.peers, id)
   168  	if peer.snapExt != nil {
   169  		ps.snapPeers--
   170  	}
   171  	return nil
   172  }
   173  
   174  // Peer retrieves the registered peer with the given id.
   175  func (ps *peerSet) Peer(id string) *peer {
   176  	ps.lock.RLock()
   177  	defer ps.lock.RUnlock()
   178  
   179  	return ps.peers[id]
   180  }
   181  
   182  func (ps *peerSet) UselessNum() int {
   183  	ps.lock.RLock()
   184  	defer ps.lock.RUnlock()
   185  
   186  	num := 0
   187  	for _, p := range ps.peers {
   188  		if p.Useless() {
   189  			num++
   190  		}
   191  	}
   192  	return num
   193  }
   194  
   195  // PeersWithoutEvent retrieves a list of peers that do not have a given event in
   196  // their set of known hashes so it might be propagated to them.
   197  func (ps *peerSet) PeersWithoutEvent(e hash.Event) []*peer {
   198  	ps.lock.RLock()
   199  	defer ps.lock.RUnlock()
   200  
   201  	list := make([]*peer, 0, len(ps.peers))
   202  	for _, p := range ps.peers {
   203  		if p.InterestedIn(e) {
   204  			list = append(list, p)
   205  		}
   206  	}
   207  	return list
   208  }
   209  
   210  // PeersWithoutTx retrieves a list of peers that do not have a given
   211  // transaction in their set of known hashes.
   212  func (ps *peerSet) PeersWithoutTx(hash common.Hash) []*peer {
   213  	ps.lock.RLock()
   214  	defer ps.lock.RUnlock()
   215  
   216  	list := make([]*peer, 0, len(ps.peers))
   217  	for _, p := range ps.peers {
   218  		if !p.knownTxs.Contains(hash) {
   219  			list = append(list, p)
   220  		}
   221  	}
   222  	return list
   223  }
   224  
   225  // List returns array of peers in the set.
   226  func (ps *peerSet) List() []*peer {
   227  	ps.lock.RLock()
   228  	defer ps.lock.RUnlock()
   229  
   230  	list := make([]*peer, 0, len(ps.peers))
   231  	for _, p := range ps.peers {
   232  		list = append(list, p)
   233  	}
   234  	return list
   235  }
   236  
   237  // Len returns if the current number of `eth` peers in the set. Since the `snap`
   238  // peers are tied to the existence of an `eth` connection, that will always be a
   239  // subset of `eth`.
   240  func (ps *peerSet) Len() int {
   241  	ps.lock.RLock()
   242  	defer ps.lock.RUnlock()
   243  
   244  	return len(ps.peers)
   245  }
   246  
   247  // SnapLen returns if the current number of `snap` peers in the set.
   248  func (ps *peerSet) SnapLen() int {
   249  	ps.lock.RLock()
   250  	defer ps.lock.RUnlock()
   251  
   252  	return ps.snapPeers
   253  }
   254  
   255  // Close disconnects all peers.
   256  func (ps *peerSet) Close() {
   257  	ps.lock.Lock()
   258  	defer ps.lock.Unlock()
   259  
   260  	for _, p := range ps.peers {
   261  		p.Disconnect(p2p.DiscQuitting)
   262  	}
   263  	ps.closed = true
   264  }