github.com/ethersphere/bee/v2@v2.2.0/pkg/hive/hive.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 hive exposes the hive protocol implementation 6 // which is the discovery protocol used to inform and be 7 // informed about other peers in the network. It gossips 8 // about all peers by default and performs no specific 9 // prioritization about which peers are gossipped to 10 // others. 11 package hive 12 13 import ( 14 "context" 15 "encoding/hex" 16 "errors" 17 "fmt" 18 "sync" 19 "time" 20 21 "golang.org/x/sync/semaphore" 22 23 "github.com/ethersphere/bee/v2/pkg/addressbook" 24 "github.com/ethersphere/bee/v2/pkg/bzz" 25 "github.com/ethersphere/bee/v2/pkg/hive/pb" 26 "github.com/ethersphere/bee/v2/pkg/log" 27 "github.com/ethersphere/bee/v2/pkg/p2p" 28 "github.com/ethersphere/bee/v2/pkg/p2p/protobuf" 29 "github.com/ethersphere/bee/v2/pkg/ratelimit" 30 "github.com/ethersphere/bee/v2/pkg/swarm" 31 ma "github.com/multiformats/go-multiaddr" 32 manet "github.com/multiformats/go-multiaddr/net" 33 ) 34 35 // loggerName is the tree path name of the logger for this package. 36 const loggerName = "hive" 37 38 const ( 39 protocolName = "hive" 40 protocolVersion = "1.1.0" 41 peersStreamName = "peers" 42 messageTimeout = 1 * time.Minute // maximum allowed time for a message to be read or written. 43 maxBatchSize = 30 44 pingTimeout = time.Second * 15 // time to wait for ping to succeed 45 batchValidationTimeout = 5 * time.Minute // prevent lock contention on peer validation 46 ) 47 48 var ( 49 limitBurst = 4 * int(swarm.MaxBins) 50 limitRate = time.Minute 51 52 ErrRateLimitExceeded = errors.New("rate limit exceeded") 53 ) 54 55 type Service struct { 56 streamer p2p.StreamerPinger 57 addressBook addressbook.GetPutter 58 addPeersHandler func(...swarm.Address) 59 networkID uint64 60 logger log.Logger 61 metrics metrics 62 inLimiter *ratelimit.Limiter 63 outLimiter *ratelimit.Limiter 64 quit chan struct{} 65 wg sync.WaitGroup 66 peersChan chan pb.Peers 67 sem *semaphore.Weighted 68 bootnode bool 69 allowPrivateCIDRs bool 70 } 71 72 func New(streamer p2p.StreamerPinger, addressbook addressbook.GetPutter, networkID uint64, bootnode bool, allowPrivateCIDRs bool, logger log.Logger) *Service { 73 svc := &Service{ 74 streamer: streamer, 75 logger: logger.WithName(loggerName).Register(), 76 addressBook: addressbook, 77 networkID: networkID, 78 metrics: newMetrics(), 79 inLimiter: ratelimit.New(limitRate, limitBurst), 80 outLimiter: ratelimit.New(limitRate, limitBurst), 81 quit: make(chan struct{}), 82 peersChan: make(chan pb.Peers), 83 sem: semaphore.NewWeighted(int64(swarm.MaxBins)), 84 bootnode: bootnode, 85 allowPrivateCIDRs: allowPrivateCIDRs, 86 } 87 88 if !bootnode { 89 svc.startCheckPeersHandler() 90 } 91 92 return svc 93 } 94 95 func (s *Service) Protocol() p2p.ProtocolSpec { 96 return p2p.ProtocolSpec{ 97 Name: protocolName, 98 Version: protocolVersion, 99 StreamSpecs: []p2p.StreamSpec{ 100 { 101 Name: peersStreamName, 102 Handler: s.peersHandler, 103 }, 104 }, 105 DisconnectIn: s.disconnect, 106 DisconnectOut: s.disconnect, 107 } 108 } 109 110 var ErrShutdownInProgress = errors.New("shutdown in progress") 111 112 func (s *Service) BroadcastPeers(ctx context.Context, addressee swarm.Address, peers ...swarm.Address) error { 113 max := maxBatchSize 114 s.metrics.BroadcastPeers.Inc() 115 s.metrics.BroadcastPeersPeers.Add(float64(len(peers))) 116 117 for len(peers) > 0 { 118 if max > len(peers) { 119 max = len(peers) 120 } 121 122 // If broadcasting limit is exceeded, return early 123 if !s.outLimiter.Allow(addressee.ByteString(), max) { 124 return nil 125 } 126 127 select { 128 case <-s.quit: 129 return ErrShutdownInProgress 130 default: 131 } 132 133 if err := s.sendPeers(ctx, addressee, peers[:max]); err != nil { 134 return err 135 } 136 137 peers = peers[max:] 138 } 139 140 return nil 141 } 142 143 func (s *Service) SetAddPeersHandler(h func(addr ...swarm.Address)) { 144 s.addPeersHandler = h 145 } 146 147 func (s *Service) Close() error { 148 close(s.quit) 149 150 stopped := make(chan struct{}) 151 go func() { 152 defer close(stopped) 153 s.wg.Wait() 154 }() 155 156 select { 157 case <-stopped: 158 return nil 159 case <-time.After(time.Second * 5): 160 return errors.New("hive: waited 5 seconds to close active goroutines") 161 } 162 } 163 164 func (s *Service) sendPeers(ctx context.Context, peer swarm.Address, peers []swarm.Address) (err error) { 165 s.metrics.BroadcastPeersSends.Inc() 166 stream, err := s.streamer.NewStream(ctx, peer, nil, protocolName, protocolVersion, peersStreamName) 167 if err != nil { 168 return fmt.Errorf("new stream: %w", err) 169 } 170 defer func() { 171 if err != nil { 172 _ = stream.Reset() 173 } else { 174 _ = stream.Close() 175 } 176 }() 177 w, _ := protobuf.NewWriterAndReader(stream) 178 var peersRequest pb.Peers 179 for _, p := range peers { 180 addr, err := s.addressBook.Get(p) 181 if err != nil { 182 if errors.Is(err, addressbook.ErrNotFound) { 183 s.logger.Debug("broadcast peers; peer not found in the addressbook, skipping...", "peer_address", p) 184 continue 185 } 186 return err 187 } 188 189 if !s.allowPrivateCIDRs && manet.IsPrivateAddr(addr.Underlay) { 190 continue // Don't advertise private CIDRs to the public network. 191 } 192 193 peersRequest.Peers = append(peersRequest.Peers, &pb.BzzAddress{ 194 Overlay: addr.Overlay.Bytes(), 195 Underlay: addr.Underlay.Bytes(), 196 Signature: addr.Signature, 197 Nonce: addr.Nonce, 198 }) 199 } 200 201 if err := w.WriteMsgWithContext(ctx, &peersRequest); err != nil { 202 return fmt.Errorf("write Peers message: %w", err) 203 } 204 205 return nil 206 } 207 208 func (s *Service) peersHandler(ctx context.Context, peer p2p.Peer, stream p2p.Stream) error { 209 s.metrics.PeersHandler.Inc() 210 _, r := protobuf.NewWriterAndReader(stream) 211 ctx, cancel := context.WithTimeout(ctx, messageTimeout) 212 defer cancel() 213 var peersReq pb.Peers 214 if err := r.ReadMsgWithContext(ctx, &peersReq); err != nil { 215 _ = stream.Reset() 216 return fmt.Errorf("read requestPeers message: %w", err) 217 } 218 219 s.metrics.PeersHandlerPeers.Add(float64(len(peersReq.Peers))) 220 221 if !s.inLimiter.Allow(peer.Address.ByteString(), len(peersReq.Peers)) { 222 _ = stream.Reset() 223 return ErrRateLimitExceeded 224 } 225 226 // close the stream before processing in order to unblock the sending side 227 // fullclose is called async because there is no need to wait for confirmation, 228 // but we still want to handle not closed stream from the other side to avoid zombie stream 229 go stream.FullClose() 230 231 if s.bootnode { 232 return nil 233 } 234 235 select { 236 case s.peersChan <- peersReq: 237 case <-s.quit: 238 return errors.New("failed to process peers, shutting down hive") 239 } 240 241 return nil 242 } 243 244 func (s *Service) disconnect(peer p2p.Peer) error { 245 s.inLimiter.Clear(peer.Address.ByteString()) 246 s.outLimiter.Clear(peer.Address.ByteString()) 247 return nil 248 } 249 250 func (s *Service) startCheckPeersHandler() { 251 ctx, cancel := context.WithCancel(context.Background()) 252 s.wg.Add(1) 253 go func() { 254 defer s.wg.Done() 255 <-s.quit 256 cancel() 257 }() 258 259 s.wg.Add(1) 260 go func() { 261 defer s.wg.Done() 262 for { 263 select { 264 case <-ctx.Done(): 265 return 266 case newPeers := <-s.peersChan: 267 s.wg.Add(1) 268 go func() { 269 defer s.wg.Done() 270 cctx, cancel := context.WithTimeout(ctx, batchValidationTimeout) 271 defer cancel() 272 s.checkAndAddPeers(cctx, newPeers) 273 }() 274 } 275 } 276 }() 277 } 278 279 func (s *Service) checkAndAddPeers(ctx context.Context, peers pb.Peers) { 280 281 var peersToAdd []swarm.Address 282 mtx := sync.Mutex{} 283 wg := sync.WaitGroup{} 284 285 addPeer := func(newPeer *pb.BzzAddress, multiUnderlay ma.Multiaddr) { 286 287 err := s.sem.Acquire(ctx, 1) 288 if err != nil { 289 return 290 } 291 292 wg.Add(1) 293 go func() { 294 s.metrics.PeerConnectAttempts.Inc() 295 296 defer func() { 297 s.sem.Release(1) 298 wg.Done() 299 }() 300 301 ctx, cancel := context.WithTimeout(ctx, pingTimeout) 302 defer cancel() 303 304 start := time.Now() 305 306 // check if the underlay is usable by doing a raw ping using libp2p 307 if _, err := s.streamer.Ping(ctx, multiUnderlay); err != nil { 308 s.metrics.PingFailureTime.Observe(time.Since(start).Seconds()) 309 s.metrics.UnreachablePeers.Inc() 310 s.logger.Debug("unreachable peer underlay", "peer_address", hex.EncodeToString(newPeer.Overlay), "underlay", multiUnderlay) 311 return 312 } 313 s.metrics.PingTime.Observe(time.Since(start).Seconds()) 314 315 s.metrics.ReachablePeers.Inc() 316 317 bzzAddress := bzz.Address{ 318 Overlay: swarm.NewAddress(newPeer.Overlay), 319 Underlay: multiUnderlay, 320 Signature: newPeer.Signature, 321 Nonce: newPeer.Nonce, 322 } 323 324 err := s.addressBook.Put(bzzAddress.Overlay, bzzAddress) 325 if err != nil { 326 s.metrics.StorePeerErr.Inc() 327 s.logger.Warning("skipping peer in response", "peer_address", newPeer.String(), "error", err) 328 return 329 } 330 331 mtx.Lock() 332 peersToAdd = append(peersToAdd, bzzAddress.Overlay) 333 mtx.Unlock() 334 }() 335 336 } 337 338 for _, p := range peers.Peers { 339 340 multiUnderlay, err := ma.NewMultiaddrBytes(p.Underlay) 341 if err != nil { 342 s.metrics.PeerUnderlayErr.Inc() 343 s.logger.Debug("multi address underlay", "error", err) 344 continue 345 } 346 347 // if peer exists already in the addressBook 348 // and if the underlays match, skip 349 addr, err := s.addressBook.Get(swarm.NewAddress(p.Overlay)) 350 if err == nil && addr.Underlay.Equal(multiUnderlay) { 351 continue 352 } 353 354 // add peer does not exist in the addressbook 355 addPeer(p, multiUnderlay) 356 } 357 wg.Wait() 358 359 if s.addPeersHandler != nil && len(peersToAdd) > 0 { 360 s.addPeersHandler(peersToAdd...) 361 } 362 }