github.com/ethersphere/bee/v2@v2.2.0/pkg/p2p/libp2p/peer.go (about)

     1  // Copyright 2020 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package libp2p
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"sort"
    11  	"sync"
    12  
    13  	"github.com/ethersphere/bee/v2/pkg/p2p"
    14  	"github.com/ethersphere/bee/v2/pkg/swarm"
    15  	"github.com/libp2p/go-libp2p/core/network"
    16  	libp2ppeer "github.com/libp2p/go-libp2p/core/peer"
    17  	ma "github.com/multiformats/go-multiaddr"
    18  )
    19  
    20  type peerRegistry struct {
    21  	underlays   map[string]libp2ppeer.ID                    // map overlay address to underlay peer id
    22  	overlays    map[libp2ppeer.ID]swarm.Address             // map underlay peer id to overlay address
    23  	full        map[libp2ppeer.ID]bool                      // map to track whether a node is full or light node (true=full)
    24  	connections map[libp2ppeer.ID]map[network.Conn]struct{} // list of connections for safe removal on Disconnect notification
    25  	streams     map[libp2ppeer.ID]map[network.Stream]context.CancelFunc
    26  	mu          sync.RWMutex
    27  
    28  	//nolint:misspell
    29  	disconnecter     disconnecter // peerRegistry notifies libp2p on peer disconnection
    30  	network.Notifiee              // peerRegistry can be the receiver for network.Notify
    31  }
    32  
    33  type disconnecter interface {
    34  	disconnected(swarm.Address)
    35  }
    36  
    37  func newPeerRegistry() *peerRegistry {
    38  	return &peerRegistry{
    39  		underlays:   make(map[string]libp2ppeer.ID),
    40  		overlays:    make(map[libp2ppeer.ID]swarm.Address),
    41  		full:        make(map[libp2ppeer.ID]bool),
    42  		connections: make(map[libp2ppeer.ID]map[network.Conn]struct{}),
    43  		streams:     make(map[libp2ppeer.ID]map[network.Stream]context.CancelFunc),
    44  
    45  		Notifiee: new(network.NoopNotifiee),
    46  	}
    47  }
    48  
    49  func (r *peerRegistry) Exists(overlay swarm.Address) (found bool) {
    50  	_, found = r.peerID(overlay)
    51  	return found
    52  }
    53  
    54  // Disconnect removes the peer from registry in disconnect.
    55  // peerRegistry has to be set by network.Network.Notify().
    56  func (r *peerRegistry) Disconnected(_ network.Network, c network.Conn) {
    57  	peerID := c.RemotePeer()
    58  
    59  	r.mu.Lock()
    60  
    61  	// remove only the related connection,
    62  	// not eventusally newly created one for the same peer
    63  	if _, ok := r.connections[peerID][c]; !ok {
    64  		r.mu.Unlock()
    65  		return
    66  	}
    67  
    68  	// if there are multiple libp2p connections, consider the node disconnected only when the last connection is disconnected
    69  	delete(r.connections[peerID], c)
    70  	if len(r.connections[peerID]) > 0 {
    71  		r.mu.Unlock()
    72  		return
    73  	}
    74  
    75  	delete(r.connections, peerID)
    76  	overlay := r.overlays[peerID]
    77  	delete(r.overlays, peerID)
    78  	delete(r.underlays, overlay.ByteString())
    79  	for _, cancel := range r.streams[peerID] {
    80  		cancel()
    81  	}
    82  	delete(r.streams, peerID)
    83  	delete(r.full, peerID)
    84  	r.mu.Unlock()
    85  	r.disconnecter.disconnected(overlay)
    86  
    87  }
    88  
    89  func (r *peerRegistry) addStream(peerID libp2ppeer.ID, stream network.Stream, cancel context.CancelFunc) {
    90  	r.mu.Lock()
    91  	defer r.mu.Unlock()
    92  	if _, ok := r.streams[peerID]; !ok {
    93  		// it is possible that an addStream will be called after a disconnect
    94  		return
    95  	}
    96  	r.streams[peerID][stream] = cancel
    97  }
    98  
    99  func (r *peerRegistry) removeStream(peerID libp2ppeer.ID, stream network.Stream) {
   100  	r.mu.Lock()
   101  	defer r.mu.Unlock()
   102  
   103  	peer, ok := r.streams[peerID]
   104  	if !ok {
   105  		return
   106  	}
   107  
   108  	cancel, ok := peer[stream]
   109  	if !ok {
   110  		return
   111  	}
   112  
   113  	cancel()
   114  
   115  	delete(r.streams[peerID], stream)
   116  }
   117  
   118  func (r *peerRegistry) peers() []p2p.Peer {
   119  	r.mu.RLock()
   120  	peers := make([]p2p.Peer, 0, len(r.overlays))
   121  	for p, a := range r.overlays {
   122  		peers = append(peers, p2p.Peer{
   123  			Address:  a,
   124  			FullNode: r.full[p],
   125  		})
   126  	}
   127  	r.mu.RUnlock()
   128  	sort.Slice(peers, func(i, j int) bool {
   129  		return bytes.Compare(peers[i].Address.Bytes(), peers[j].Address.Bytes()) == -1
   130  	})
   131  	return peers
   132  }
   133  
   134  func (r *peerRegistry) addIfNotExists(c network.Conn, overlay swarm.Address, full bool) (exists bool) {
   135  	peerID := c.RemotePeer()
   136  	r.mu.Lock()
   137  	defer r.mu.Unlock()
   138  
   139  	if _, ok := r.connections[peerID]; !ok {
   140  		r.connections[peerID] = make(map[network.Conn]struct{})
   141  	}
   142  	// the connection is added even if the peer already exists in peer registry
   143  	// this is solving a case of multiple underlying libp2p connections for the same peer
   144  	r.connections[peerID][c] = struct{}{}
   145  
   146  	if _, exists := r.underlays[overlay.ByteString()]; exists {
   147  		return true
   148  	}
   149  
   150  	r.streams[peerID] = make(map[network.Stream]context.CancelFunc)
   151  	r.underlays[overlay.ByteString()] = peerID
   152  	r.overlays[peerID] = overlay
   153  	r.full[peerID] = full
   154  	return false
   155  
   156  }
   157  
   158  func (r *peerRegistry) peerID(overlay swarm.Address) (peerID libp2ppeer.ID, found bool) {
   159  	r.mu.RLock()
   160  	peerID, found = r.underlays[overlay.ByteString()]
   161  	r.mu.RUnlock()
   162  	return peerID, found
   163  }
   164  
   165  func (r *peerRegistry) overlay(peerID libp2ppeer.ID) (swarm.Address, bool) {
   166  	r.mu.RLock()
   167  	overlay, found := r.overlays[peerID]
   168  	r.mu.RUnlock()
   169  	return overlay, found
   170  }
   171  
   172  func (r *peerRegistry) fullnode(peerID libp2ppeer.ID) (bool, bool) {
   173  	r.mu.RLock()
   174  	full, found := r.full[peerID]
   175  	r.mu.RUnlock()
   176  	return full, found
   177  }
   178  
   179  func (r *peerRegistry) isConnected(peerID libp2ppeer.ID, remoteAddr ma.Multiaddr) (swarm.Address, bool) {
   180  	if remoteAddr == nil {
   181  		return swarm.ZeroAddress, false
   182  	}
   183  
   184  	r.mu.RLock()
   185  	defer r.mu.RUnlock()
   186  
   187  	overlay, found := r.overlays[peerID]
   188  	if !found {
   189  		return swarm.ZeroAddress, false
   190  	}
   191  
   192  	// check connection remote address
   193  	conns, ok := r.connections[peerID]
   194  	if !ok {
   195  		return swarm.ZeroAddress, false
   196  	}
   197  
   198  	for c := range conns {
   199  		if c.RemoteMultiaddr().Equal(remoteAddr) {
   200  			// we ARE connected to the peer on expected address
   201  			return overlay, true
   202  		}
   203  	}
   204  
   205  	return swarm.ZeroAddress, false
   206  }
   207  
   208  func (r *peerRegistry) remove(overlay swarm.Address) (found, full bool, peerID libp2ppeer.ID) {
   209  	r.mu.Lock()
   210  	peerID, found = r.underlays[overlay.ByteString()]
   211  	delete(r.overlays, peerID)
   212  	delete(r.underlays, overlay.ByteString())
   213  	delete(r.connections, peerID)
   214  	for _, cancel := range r.streams[peerID] {
   215  		cancel()
   216  	}
   217  	delete(r.streams, peerID)
   218  	full = r.full[peerID]
   219  	delete(r.full, peerID)
   220  	r.mu.Unlock()
   221  
   222  	return found, full, peerID
   223  }
   224  
   225  func (r *peerRegistry) setDisconnecter(d disconnecter) {
   226  	r.disconnecter = d
   227  }