github.com/ethersphere/bee/v2@v2.2.0/pkg/topology/kademlia/kademlia.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 kademlia 6 7 import ( 8 "context" 9 random "crypto/rand" 10 "encoding/json" 11 "errors" 12 "fmt" 13 "math/big" 14 "math/rand" 15 "path/filepath" 16 "sync" 17 "time" 18 19 "github.com/ethersphere/bee/v2/pkg/addressbook" 20 "github.com/ethersphere/bee/v2/pkg/discovery" 21 "github.com/ethersphere/bee/v2/pkg/log" 22 "github.com/ethersphere/bee/v2/pkg/p2p" 23 "github.com/ethersphere/bee/v2/pkg/shed" 24 "github.com/ethersphere/bee/v2/pkg/swarm" 25 "github.com/ethersphere/bee/v2/pkg/topology" 26 im "github.com/ethersphere/bee/v2/pkg/topology/kademlia/internal/metrics" 27 "github.com/ethersphere/bee/v2/pkg/topology/kademlia/internal/waitnext" 28 "github.com/ethersphere/bee/v2/pkg/topology/pslice" 29 "github.com/ethersphere/bee/v2/pkg/util/ioutil" 30 ma "github.com/multiformats/go-multiaddr" 31 "golang.org/x/sync/errgroup" 32 ) 33 34 // loggerName is the tree path name of the logger for this package. 35 const loggerName = "kademlia" 36 37 const ( 38 maxConnAttempts = 1 // when there is maxConnAttempts failed connect calls for a given peer it is considered non-connectable 39 maxBootNodeAttempts = 3 // how many attempts to dial to boot-nodes before giving up 40 maxNeighborAttempts = 3 // how many attempts to dial to boot-nodes before giving up 41 42 addPeerBatchSize = 500 43 44 // To avoid context.Timeout errors during network failure, the value of 45 // the peerConnectionAttemptTimeout constant must be equal to or greater 46 // than 5 seconds (empirically verified). 47 peerConnectionAttemptTimeout = 15 * time.Second // timeout for establishing a new connection with peer. 48 ) 49 50 // Default option values 51 const ( 52 defaultBitSuffixLength = 4 // the number of bits used to create pseudo addresses for balancing, 2^4, 16 addresses 53 defaultLowWaterMark = 3 // the number of peers in consecutive deepest bins that constitute as nearest neighbours 54 defaultSaturationPeers = 8 55 defaultOverSaturationPeers = 18 56 defaultBootNodeOverSaturationPeers = 20 57 defaultShortRetry = 30 * time.Second 58 defaultTimeToRetry = 2 * defaultShortRetry 59 defaultPruneWakeup = 5 * time.Minute 60 defaultBroadcastBinSize = 2 61 ) 62 63 var ( 64 errOverlayMismatch = errors.New("overlay mismatch") 65 errPruneEntry = errors.New("prune entry") 66 errEmptyBin = errors.New("empty bin") 67 errAnnounceLightNode = errors.New("announcing light node") 68 ) 69 70 type ( 71 binSaturationFunc func(bin uint8, connected *pslice.PSlice, exclude peerExcludeFunc) bool 72 sanctionedPeerFunc func(peer swarm.Address) bool 73 pruneFunc func(depth uint8) 74 pruneCountFunc func(bin uint8, connected *pslice.PSlice, exclude peerExcludeFunc) (int, int) 75 staticPeerFunc func(peer swarm.Address) bool 76 peerExcludeFunc func(peer swarm.Address) bool 77 excludeFunc func(...im.ExcludeOp) peerExcludeFunc 78 ) 79 80 var noopSanctionedPeerFn = func(_ swarm.Address) bool { return false } 81 82 // Options for injecting services to Kademlia. 83 type Options struct { 84 SaturationFunc binSaturationFunc 85 PruneCountFunc pruneCountFunc 86 Bootnodes []ma.Multiaddr 87 BootnodeMode bool 88 PruneFunc pruneFunc 89 StaticNodes []swarm.Address 90 ExcludeFunc excludeFunc 91 DataDir string 92 93 BitSuffixLength *int 94 TimeToRetry *time.Duration 95 ShortRetry *time.Duration 96 PruneWakeup *time.Duration 97 SaturationPeers *int 98 OverSaturationPeers *int 99 BootnodeOverSaturationPeers *int 100 BroadcastBinSize *int 101 LowWaterMark *int 102 } 103 104 // kadOptions are made from Options with default values set 105 type kadOptions struct { 106 SaturationFunc binSaturationFunc 107 Bootnodes []ma.Multiaddr 108 BootnodeMode bool 109 PruneCountFunc pruneCountFunc 110 PruneFunc pruneFunc 111 StaticNodes []swarm.Address 112 ExcludeFunc excludeFunc 113 114 TimeToRetry time.Duration 115 ShortRetry time.Duration 116 PruneWakeup time.Duration 117 BitSuffixLength int // additional depth of common prefix for bin 118 SaturationPeers int 119 OverSaturationPeers int 120 BootnodeOverSaturationPeers int 121 BroadcastBinSize int 122 LowWaterMark int 123 } 124 125 func newKadOptions(o Options) kadOptions { 126 ko := kadOptions{ 127 // copy values 128 SaturationFunc: o.SaturationFunc, 129 Bootnodes: o.Bootnodes, 130 BootnodeMode: o.BootnodeMode, 131 PruneFunc: o.PruneFunc, 132 StaticNodes: o.StaticNodes, 133 ExcludeFunc: o.ExcludeFunc, 134 // copy or use default 135 TimeToRetry: defaultValDuration(o.TimeToRetry, defaultTimeToRetry), 136 ShortRetry: defaultValDuration(o.ShortRetry, defaultShortRetry), 137 PruneWakeup: defaultValDuration(o.PruneWakeup, defaultPruneWakeup), 138 BitSuffixLength: defaultValInt(o.BitSuffixLength, defaultBitSuffixLength), 139 SaturationPeers: defaultValInt(o.SaturationPeers, defaultSaturationPeers), 140 OverSaturationPeers: defaultValInt(o.OverSaturationPeers, defaultOverSaturationPeers), 141 BootnodeOverSaturationPeers: defaultValInt(o.BootnodeOverSaturationPeers, defaultBootNodeOverSaturationPeers), 142 BroadcastBinSize: defaultValInt(o.BroadcastBinSize, defaultBroadcastBinSize), 143 LowWaterMark: defaultValInt(o.LowWaterMark, defaultLowWaterMark), 144 } 145 146 if ko.SaturationFunc == nil { 147 ko.SaturationFunc = makeSaturationFunc(ko) 148 } 149 150 return ko 151 } 152 153 func defaultValInt(v *int, d int) int { 154 if v == nil { 155 return d 156 } 157 return *v 158 } 159 160 func defaultValDuration(v *time.Duration, d time.Duration) time.Duration { 161 if v == nil { 162 return d 163 } 164 return *v 165 } 166 167 func makeSaturationFunc(o kadOptions) binSaturationFunc { 168 os := o.OverSaturationPeers 169 if o.BootnodeMode { 170 os = o.BootnodeOverSaturationPeers 171 } 172 return binSaturated(os, isStaticPeer(o.StaticNodes)) 173 } 174 175 // Kad is the Swarm forwarding kademlia implementation. 176 type Kad struct { 177 opt kadOptions 178 base swarm.Address // this node's overlay address 179 discovery discovery.Driver // the discovery driver 180 addressBook addressbook.Interface // address book to get underlays 181 p2p p2p.Service // p2p service to connect to nodes with 182 commonBinPrefixes [][]swarm.Address // list of address prefixes for each bin 183 connectedPeers *pslice.PSlice // a slice of peers sorted and indexed by po, indexes kept in `bins` 184 knownPeers *pslice.PSlice // both are po aware slice of addresses 185 depth uint8 // current neighborhood depth 186 storageRadius uint8 // storage area of responsibility 187 depthMu sync.RWMutex // protect depth changes 188 manageC chan struct{} // trigger the manage forever loop to connect to new peers 189 peerSig []chan struct{} 190 peerSigMtx sync.Mutex 191 logger log.Logger // logger 192 bootnode bool // indicates whether the node is working in bootnode mode 193 collector *im.Collector 194 quit chan struct{} // quit channel 195 halt chan struct{} // halt channel 196 done chan struct{} // signal that `manage` has quit 197 wg sync.WaitGroup 198 waitNext *waitnext.WaitNext 199 metrics metrics 200 staticPeer staticPeerFunc 201 bgBroadcastCtx context.Context 202 bgBroadcastCancel context.CancelFunc 203 reachability p2p.ReachabilityStatus 204 } 205 206 // New returns a new Kademlia. 207 func New( 208 base swarm.Address, 209 addressbook addressbook.Interface, 210 discovery discovery.Driver, 211 p2pSvc p2p.Service, 212 logger log.Logger, 213 o Options, 214 ) (*Kad, error) { 215 var k *Kad 216 217 if o.DataDir == "" { 218 logger.Warning("using in-mem store for kademlia metrics, no state will be persisted") 219 } else { 220 o.DataDir = filepath.Join(o.DataDir, ioutil.DataPathKademlia) 221 } 222 sdb, err := shed.NewDB(o.DataDir, nil) 223 if err != nil { 224 return nil, fmt.Errorf("unable to create metrics storage: %w", err) 225 } 226 imc, err := im.NewCollector(sdb) 227 if err != nil { 228 return nil, fmt.Errorf("unable to create metrics collector: %w", err) 229 } 230 231 opt := newKadOptions(o) 232 233 k = &Kad{ 234 opt: opt, 235 base: base, 236 discovery: discovery, 237 addressBook: addressbook, 238 p2p: p2pSvc, 239 commonBinPrefixes: make([][]swarm.Address, int(swarm.MaxBins)), 240 connectedPeers: pslice.New(int(swarm.MaxBins), base), 241 knownPeers: pslice.New(int(swarm.MaxBins), base), 242 manageC: make(chan struct{}, 1), 243 waitNext: waitnext.New(), 244 logger: logger.WithName(loggerName).Register(), 245 bootnode: opt.BootnodeMode, 246 collector: imc, 247 quit: make(chan struct{}), 248 halt: make(chan struct{}), 249 done: make(chan struct{}), 250 metrics: newMetrics(), 251 staticPeer: isStaticPeer(opt.StaticNodes), 252 storageRadius: swarm.MaxPO, 253 } 254 255 if k.opt.PruneFunc == nil { 256 k.opt.PruneFunc = k.pruneOversaturatedBins 257 } 258 259 os := k.opt.OverSaturationPeers 260 if k.opt.BootnodeMode { 261 os = k.opt.BootnodeOverSaturationPeers 262 } 263 k.opt.PruneCountFunc = binPruneCount(os, isStaticPeer(k.opt.StaticNodes)) 264 265 if k.opt.ExcludeFunc == nil { 266 k.opt.ExcludeFunc = func(f ...im.ExcludeOp) peerExcludeFunc { 267 return func(peer swarm.Address) bool { 268 return k.collector.Exclude(peer, f...) 269 } 270 } 271 } 272 273 if k.opt.BitSuffixLength > 0 { 274 k.commonBinPrefixes = generateCommonBinPrefixes(k.base, k.opt.BitSuffixLength) 275 } 276 277 k.bgBroadcastCtx, k.bgBroadcastCancel = context.WithCancel(context.Background()) 278 279 k.metrics.ReachabilityStatus.WithLabelValues(p2p.ReachabilityStatusUnknown.String()).Set(0) 280 return k, nil 281 } 282 283 type peerConnInfo struct { 284 po uint8 285 addr swarm.Address 286 } 287 288 // connectBalanced attempts to connect to the balanced peers first. 289 func (k *Kad) connectBalanced(wg *sync.WaitGroup, peerConnChan chan<- *peerConnInfo) { 290 skipPeers := func(peer swarm.Address) bool { 291 if k.waitNext.Waiting(peer) { 292 k.metrics.TotalBeforeExpireWaits.Inc() 293 return true 294 } 295 return false 296 } 297 298 depth := k.neighborhoodDepth() 299 300 for i := range k.commonBinPrefixes { 301 302 binPeersLength := k.knownPeers.BinSize(uint8(i)) 303 304 // balancer should skip on bins where neighborhood connector would connect to peers anyway 305 // and there are not enough peers in known addresses to properly balance the bin 306 if i >= int(depth) && binPeersLength < len(k.commonBinPrefixes[i]) { 307 continue 308 } 309 310 binPeers := k.knownPeers.BinPeers(uint8(i)) 311 binConnectedPeers := k.connectedPeers.BinPeers(uint8(i)) 312 313 for j := range k.commonBinPrefixes[i] { 314 pseudoAddr := k.commonBinPrefixes[i][j] 315 316 // Connect to closest known peer which we haven't tried connecting to recently. 317 318 _, exists := nClosePeerInSlice(binConnectedPeers, pseudoAddr, noopSanctionedPeerFn, uint8(i+k.opt.BitSuffixLength+1)) 319 if exists { 320 continue 321 } 322 323 closestKnownPeer, exists := nClosePeerInSlice(binPeers, pseudoAddr, skipPeers, uint8(i+k.opt.BitSuffixLength+1)) 324 if !exists { 325 continue 326 } 327 328 if k.connectedPeers.Exists(closestKnownPeer) { 329 continue 330 } 331 332 blocklisted, err := k.p2p.Blocklisted(closestKnownPeer) 333 if err != nil { 334 k.logger.Warning("peer blocklist check failed", "error", err) 335 } 336 if blocklisted { 337 continue 338 } 339 340 wg.Add(1) 341 select { 342 case peerConnChan <- &peerConnInfo{ 343 po: swarm.Proximity(k.base.Bytes(), closestKnownPeer.Bytes()), 344 addr: closestKnownPeer, 345 }: 346 case <-k.quit: 347 wg.Done() 348 return 349 } 350 } 351 } 352 } 353 354 // connectNeighbours attempts to connect to the neighbours 355 // which were not considered by the connectBalanced method. 356 func (k *Kad) connectNeighbours(wg *sync.WaitGroup, peerConnChan chan<- *peerConnInfo) { 357 358 sent := 0 359 var currentPo uint8 = 0 360 361 _ = k.knownPeers.EachBinRev(func(addr swarm.Address, po uint8) (bool, bool, error) { 362 363 // out of depth, skip bin 364 if po < k.neighborhoodDepth() { 365 return false, true, nil 366 } 367 368 if po != currentPo { 369 currentPo = po 370 sent = 0 371 } 372 373 if k.connectedPeers.Exists(addr) { 374 return false, false, nil 375 } 376 377 blocklisted, err := k.p2p.Blocklisted(addr) 378 if err != nil { 379 k.logger.Warning("peer blocklist check failed", "error", err) 380 } 381 if blocklisted { 382 return false, false, nil 383 } 384 385 if k.waitNext.Waiting(addr) { 386 k.metrics.TotalBeforeExpireWaits.Inc() 387 return false, false, nil 388 } 389 390 wg.Add(1) 391 select { 392 case peerConnChan <- &peerConnInfo{po: po, addr: addr}: 393 case <-k.quit: 394 wg.Done() 395 return true, false, nil 396 } 397 398 sent++ 399 400 // We want 'sent' equal to 'saturationPeers' 401 // in order to skip to the next bin and speed up the topology build. 402 return false, sent == k.opt.SaturationPeers, nil 403 }) 404 } 405 406 // connectionAttemptsHandler handles the connection attempts 407 // to peers sent by the producers to the peerConnChan. 408 func (k *Kad) connectionAttemptsHandler(ctx context.Context, wg *sync.WaitGroup, neighbourhoodChan, balanceChan <-chan *peerConnInfo) { 409 connect := func(peer *peerConnInfo) { 410 bzzAddr, err := k.addressBook.Get(peer.addr) 411 switch { 412 case errors.Is(err, addressbook.ErrNotFound): 413 k.logger.Debug("empty address book entry for peer", "peer_address", peer.addr) 414 k.knownPeers.Remove(peer.addr) 415 return 416 case err != nil: 417 k.logger.Debug("failed to get address book entry for peer", "peer_address", peer.addr, "error", err) 418 return 419 } 420 421 remove := func(peer *peerConnInfo) { 422 k.waitNext.Remove(peer.addr) 423 k.knownPeers.Remove(peer.addr) 424 if err := k.addressBook.Remove(peer.addr); err != nil { 425 k.logger.Debug("could not remove peer from addressbook", "peer_address", peer.addr) 426 } 427 } 428 429 switch err = k.connect(ctx, peer.addr, bzzAddr.Underlay); { 430 case errors.Is(err, p2p.ErrNetworkUnavailable): 431 k.logger.Debug("network unavailable when reaching peer", "peer_overlay_address", peer.addr, "peer_underlay_address", bzzAddr.Underlay) 432 return 433 case errors.Is(err, errPruneEntry): 434 k.logger.Debug("dial to light node", "peer_overlay_address", peer.addr, "peer_underlay_address", bzzAddr.Underlay) 435 remove(peer) 436 return 437 case errors.Is(err, errOverlayMismatch): 438 k.logger.Debug("overlay mismatch has occurred", "peer_overlay_address", peer.addr, "peer_underlay_address", bzzAddr.Underlay) 439 remove(peer) 440 return 441 case errors.Is(err, p2p.ErrPeerBlocklisted): 442 k.logger.Debug("peer still in blocklist", "peer_address", bzzAddr) 443 k.logger.Warning("peer still in blocklist") 444 return 445 case err != nil: 446 k.logger.Debug("peer not reachable from kademlia", "peer_address", bzzAddr, "error", err) 447 k.logger.Warning("peer not reachable when attempting to connect") 448 return 449 } 450 451 k.waitNext.Set(peer.addr, time.Now().Add(k.opt.ShortRetry), 0) 452 453 k.connectedPeers.Add(peer.addr) 454 455 k.metrics.TotalOutboundConnections.Inc() 456 k.collector.Record(peer.addr, im.PeerLogIn(time.Now(), im.PeerConnectionDirectionOutbound)) 457 458 k.recalcDepth() 459 460 k.logger.Info("connected to peer", "peer_address", peer.addr, "proximity_order", peer.po) 461 k.notifyManageLoop() 462 k.notifyPeerSig() 463 } 464 465 var ( 466 // The inProgress helps to avoid making a connection 467 // to a peer who has the connection already in progress. 468 inProgress = make(map[string]bool) 469 inProgressMu sync.Mutex 470 ) 471 connAttempt := func(peerConnChan <-chan *peerConnInfo) { 472 for { 473 select { 474 case <-k.quit: 475 return 476 case peer := <-peerConnChan: 477 addr := peer.addr.String() 478 479 if k.waitNext.Waiting(peer.addr) { 480 k.metrics.TotalBeforeExpireWaits.Inc() 481 wg.Done() 482 continue 483 } 484 485 inProgressMu.Lock() 486 if !inProgress[addr] { 487 inProgress[addr] = true 488 inProgressMu.Unlock() 489 connect(peer) 490 inProgressMu.Lock() 491 delete(inProgress, addr) 492 } 493 inProgressMu.Unlock() 494 wg.Done() 495 } 496 } 497 } 498 for i := 0; i < 32; i++ { 499 go connAttempt(balanceChan) 500 } 501 for i := 0; i < 32; i++ { 502 go connAttempt(neighbourhoodChan) 503 } 504 } 505 506 // notifyManageLoop notifies kademlia manage loop. 507 func (k *Kad) notifyManageLoop() { 508 select { 509 case k.manageC <- struct{}{}: 510 default: 511 } 512 } 513 514 // manage is a forever loop that manages the connection to new peers 515 // once they get added or once others leave. 516 func (k *Kad) manage() { 517 loggerV1 := k.logger.V(1).Register() 518 519 defer k.wg.Done() 520 defer close(k.done) 521 defer k.logger.Debug("kademlia manage loop exited") 522 523 ctx, cancel := context.WithCancel(context.Background()) 524 go func() { 525 <-k.quit 526 cancel() 527 }() 528 529 // The wg makes sure that we wait for all the connection attempts, 530 // spun up by goroutines, to finish before we try the boot-nodes. 531 var wg sync.WaitGroup 532 neighbourhoodChan := make(chan *peerConnInfo) 533 balanceChan := make(chan *peerConnInfo) 534 go k.connectionAttemptsHandler(ctx, &wg, neighbourhoodChan, balanceChan) 535 536 k.wg.Add(1) 537 go func() { 538 defer k.wg.Done() 539 for { 540 select { 541 case <-k.halt: 542 return 543 case <-k.quit: 544 return 545 case <-time.After(k.opt.PruneWakeup): 546 k.opt.PruneFunc(k.neighborhoodDepth()) 547 } 548 } 549 }() 550 551 k.wg.Add(1) 552 go func() { 553 defer k.wg.Done() 554 for { 555 select { 556 case <-k.halt: 557 return 558 case <-k.quit: 559 return 560 case <-time.After(5 * time.Minute): 561 start := time.Now() 562 loggerV1.Debug("starting to flush metrics", "start_time", start) 563 if err := k.collector.Flush(); err != nil { 564 k.metrics.InternalMetricsFlushTotalErrors.Inc() 565 k.logger.Debug("unable to flush metrics counters to the persistent store", "error", err) 566 } else { 567 k.metrics.InternalMetricsFlushTime.Observe(time.Since(start).Seconds()) 568 loggerV1.Debug("flush metrics done", "elapsed", time.Since(start)) 569 } 570 } 571 } 572 }() 573 574 // tell each neighbor about other neighbors periodically 575 k.wg.Add(1) 576 go func() { 577 defer k.wg.Done() 578 for { 579 select { 580 case <-k.halt: 581 return 582 case <-k.quit: 583 return 584 case <-time.After(15 * time.Minute): 585 var neighbors []swarm.Address 586 _ = k.connectedPeers.EachBin(func(addr swarm.Address, bin uint8) (stop bool, jumpToNext bool, err error) { 587 if bin < k.neighborhoodDepth() { 588 return true, false, nil 589 } 590 neighbors = append(neighbors, addr) 591 return false, false, nil 592 }) 593 for i, peer := range neighbors { 594 if err := k.discovery.BroadcastPeers(ctx, peer, append(neighbors[:i], neighbors[i+1:]...)...); err != nil { 595 k.logger.Debug("broadcast neighborhood failure", "peer_address", peer, "error", err) 596 } 597 } 598 } 599 } 600 }() 601 602 for { 603 select { 604 case <-k.quit: 605 return 606 case <-time.After(15 * time.Second): 607 k.notifyManageLoop() 608 case <-k.manageC: 609 start := time.Now() 610 611 select { 612 case <-k.halt: 613 // halt stops dial-outs while shutting down 614 return 615 case <-k.quit: 616 return 617 default: 618 } 619 620 if k.bootnode { 621 depth := k.neighborhoodDepth() 622 623 k.metrics.CurrentDepth.Set(float64(depth)) 624 k.metrics.CurrentlyKnownPeers.Set(float64(k.knownPeers.Length())) 625 k.metrics.CurrentlyConnectedPeers.Set(float64(k.connectedPeers.Length())) 626 627 continue 628 } 629 630 oldDepth := k.neighborhoodDepth() 631 k.connectBalanced(&wg, balanceChan) 632 k.connectNeighbours(&wg, neighbourhoodChan) 633 wg.Wait() 634 635 depth := k.neighborhoodDepth() 636 637 loggerV1.Debug("connector finished", "elapsed", time.Since(start), "old_depth", oldDepth, "new_depth", depth) 638 639 k.metrics.CurrentDepth.Set(float64(depth)) 640 k.metrics.CurrentlyKnownPeers.Set(float64(k.knownPeers.Length())) 641 k.metrics.CurrentlyConnectedPeers.Set(float64(k.connectedPeers.Length())) 642 643 if k.connectedPeers.Length() == 0 { 644 select { 645 case <-k.halt: 646 continue 647 default: 648 } 649 k.logger.Debug("kademlia: no connected peers, trying bootnodes") 650 k.connectBootNodes(ctx) 651 } else { 652 rs := make(map[string]float64) 653 ss := k.collector.Snapshot(time.Now()) 654 655 if err := k.connectedPeers.EachBin(func(addr swarm.Address, _ uint8) (bool, bool, error) { 656 if ss, ok := ss[addr.ByteString()]; ok { 657 rs[ss.Reachability.String()]++ 658 } 659 return false, false, nil 660 }); err != nil { 661 k.logger.Error(err, "unable to set peers reachability status") 662 } 663 664 for status, count := range rs { 665 k.metrics.PeersReachabilityStatus.WithLabelValues(status).Set(count) 666 } 667 } 668 } 669 } 670 } 671 672 // pruneOversaturatedBins disconnects out of depth peers from oversaturated bins 673 // while maintaining the balance of the bin and favoring healthy and reachable peers. 674 func (k *Kad) pruneOversaturatedBins(depth uint8) { 675 676 for i := range k.commonBinPrefixes { 677 678 if i >= int(depth) { 679 return 680 } 681 682 // skip to next bin if prune count is zero or fewer 683 oldCount, pruneCount := k.opt.PruneCountFunc(uint8(i), k.connectedPeers, k.opt.ExcludeFunc(im.Reachability(false))) 684 if pruneCount <= 0 { 685 continue 686 } 687 688 for j := 0; j < len(k.commonBinPrefixes[i]); j++ { 689 690 // skip to next bin if prune count is zero or fewer 691 _, pruneCount := k.opt.PruneCountFunc(uint8(i), k.connectedPeers, k.opt.ExcludeFunc(im.Reachability(false))) 692 if pruneCount <= 0 { 693 break 694 } 695 696 binPeers := k.connectedPeers.BinPeers(uint8(i)) 697 peers := k.balancedSlotPeers(k.commonBinPrefixes[i][j], binPeers, i) 698 if len(peers) <= 1 { 699 continue 700 } 701 702 var disconnectPeer = swarm.ZeroAddress 703 var unreachablePeer = swarm.ZeroAddress 704 for _, peer := range peers { 705 if ss := k.collector.Inspect(peer); ss != nil { 706 if !ss.Healthy { 707 disconnectPeer = peer 708 break 709 } 710 if ss.Reachability != p2p.ReachabilityStatusPublic { 711 unreachablePeer = peer 712 } 713 } 714 } 715 716 if disconnectPeer.IsZero() { 717 if unreachablePeer.IsZero() { 718 disconnectPeer = peers[rand.Intn(len(peers))] 719 } else { 720 disconnectPeer = unreachablePeer // pick unreachable peer 721 } 722 } 723 724 err := k.p2p.Disconnect(disconnectPeer, "pruned from oversaturated bin") 725 if err != nil { 726 k.logger.Debug("prune disconnect failed", "error", err) 727 } 728 } 729 730 newCount, _ := k.opt.PruneCountFunc(uint8(i), k.connectedPeers, k.opt.ExcludeFunc(im.Reachability(false))) 731 732 k.logger.Debug("pruning", "bin", i, "oldBinSize", oldCount, "newBinSize", newCount) 733 } 734 } 735 736 func (k *Kad) balancedSlotPeers(pseudoAddr swarm.Address, peers []swarm.Address, po int) []swarm.Address { 737 738 var ret []swarm.Address 739 740 for _, peer := range peers { 741 if int(swarm.ExtendedProximity(peer.Bytes(), pseudoAddr.Bytes())) >= po+k.opt.BitSuffixLength+1 { 742 ret = append(ret, peer) 743 } 744 } 745 746 return ret 747 } 748 749 func (k *Kad) Start(_ context.Context) error { 750 k.wg.Add(1) 751 go k.manage() 752 753 k.AddPeers(k.previouslyConnected()...) 754 755 go func() { 756 select { 757 case <-k.halt: 758 return 759 case <-k.quit: 760 return 761 default: 762 } 763 var ( 764 start = time.Now() 765 addresses []swarm.Address 766 ) 767 768 err := k.addressBook.IterateOverlays(func(addr swarm.Address) (stop bool, err error) { 769 addresses = append(addresses, addr) 770 if len(addresses) == addPeerBatchSize { 771 k.AddPeers(addresses...) 772 addresses = nil 773 } 774 return false, nil 775 }) 776 if err != nil { 777 k.logger.Error(err, "addressbook iterate overlays failed") 778 return 779 } 780 k.AddPeers(addresses...) 781 k.metrics.StartAddAddressBookOverlaysTime.Observe(time.Since(start).Seconds()) 782 }() 783 784 // trigger the first manage loop immediately so that 785 // we can start connecting to the bootnode quickly 786 k.notifyManageLoop() 787 788 return nil 789 } 790 791 func (k *Kad) previouslyConnected() []swarm.Address { 792 loggerV1 := k.logger.V(1).Register() 793 794 now := time.Now() 795 ss := k.collector.Snapshot(now) 796 loggerV1.Debug("metrics snapshot taken", "elapsed", time.Since(now)) 797 798 var peers []swarm.Address 799 800 for addr, p := range ss { 801 if p.ConnectionTotalDuration > 0 { 802 peers = append(peers, swarm.NewAddress([]byte(addr))) 803 } 804 } 805 806 return peers 807 } 808 809 func (k *Kad) connectBootNodes(ctx context.Context) { 810 loggerV1 := k.logger.V(1).Register() 811 812 var attempts, connected int 813 totalAttempts := maxBootNodeAttempts * len(k.opt.Bootnodes) 814 815 ctx, cancel := context.WithTimeout(ctx, 15*time.Second) 816 defer cancel() 817 818 for _, addr := range k.opt.Bootnodes { 819 if attempts >= totalAttempts || connected >= 3 { 820 return 821 } 822 823 if _, err := p2p.Discover(ctx, addr, func(addr ma.Multiaddr) (stop bool, err error) { 824 loggerV1.Debug("connecting to bootnode", "bootnode_address", addr) 825 if attempts >= maxBootNodeAttempts { 826 return true, nil 827 } 828 bzzAddress, err := k.p2p.Connect(ctx, addr) 829 830 attempts++ 831 k.metrics.TotalBootNodesConnectionAttempts.Inc() 832 833 if err != nil { 834 if !errors.Is(err, p2p.ErrAlreadyConnected) { 835 k.logger.Debug("connect to bootnode failed", "bootnode_address", addr, "error", err) 836 k.logger.Warning("connect to bootnode failed", "bootnode_address", addr) 837 return false, err 838 } 839 k.logger.Debug("connect to bootnode failed", "bootnode_address", addr, "error", err) 840 return false, nil 841 } 842 843 if err := k.onConnected(ctx, bzzAddress.Overlay); err != nil { 844 return false, err 845 } 846 847 k.metrics.TotalOutboundConnections.Inc() 848 k.collector.Record(bzzAddress.Overlay, im.PeerLogIn(time.Now(), im.PeerConnectionDirectionOutbound)) 849 loggerV1.Debug("connected to bootnode", "bootnode_address", addr) 850 connected++ 851 852 // connect to max 3 bootnodes 853 return connected >= 3, nil 854 }); err != nil && !errors.Is(err, context.Canceled) { 855 k.logger.Debug("discover to bootnode failed", "bootnode_address", addr, "error", err) 856 k.logger.Warning("discover to bootnode failed", "bootnode_address", addr) 857 return 858 } 859 } 860 } 861 862 // binSaturated indicates whether a certain bin is saturated or not. 863 // when a bin is not saturated it means we would like to proactively 864 // initiate connections to other peers in the bin. 865 func binSaturated(oversaturationAmount int, staticNode staticPeerFunc) binSaturationFunc { 866 return func(bin uint8, connected *pslice.PSlice, exclude peerExcludeFunc) bool { 867 size := 0 868 _ = connected.EachBin(func(addr swarm.Address, po uint8) (bool, bool, error) { 869 if po == bin && !exclude(addr) && !staticNode(addr) { 870 size++ 871 } 872 return false, false, nil 873 }) 874 875 return size >= oversaturationAmount 876 } 877 } 878 879 // binPruneCount counts how many peers should be pruned from a bin. 880 func binPruneCount(oversaturationAmount int, staticNode staticPeerFunc) pruneCountFunc { 881 return func(bin uint8, connected *pslice.PSlice, exclude peerExcludeFunc) (int, int) { 882 size := 0 883 _ = connected.EachBin(func(addr swarm.Address, po uint8) (bool, bool, error) { 884 if po == bin && !exclude(addr) && !staticNode(addr) { 885 size++ 886 } 887 return false, false, nil 888 }) 889 890 return size, size - oversaturationAmount 891 } 892 } 893 894 // recalcDepth calculates, assigns the new depth, and returns if depth has changed 895 func (k *Kad) recalcDepth() { 896 897 k.depthMu.Lock() 898 defer k.depthMu.Unlock() 899 900 var ( 901 peers = k.connectedPeers 902 exclude = k.opt.ExcludeFunc(im.Reachability(false)) 903 binCount = 0 904 shallowestUnsaturated = uint8(0) 905 depth uint8 906 ) 907 908 // handle edge case separately 909 if peers.Length() <= k.opt.LowWaterMark { 910 k.depth = 0 911 return 912 } 913 914 _ = peers.EachBinRev(func(addr swarm.Address, bin uint8) (bool, bool, error) { 915 if exclude(addr) { 916 return false, false, nil 917 } 918 if bin == shallowestUnsaturated { 919 binCount++ 920 return false, false, nil 921 } 922 if bin > shallowestUnsaturated && binCount < k.opt.SaturationPeers { 923 // this means we have less than quickSaturationPeers in the previous bin 924 // therefore we can return assuming that bin is the unsaturated one. 925 return true, false, nil 926 } 927 shallowestUnsaturated = bin 928 binCount = 1 929 930 return false, false, nil 931 }) 932 depth = shallowestUnsaturated 933 934 shallowestEmpty, noEmptyBins := peers.ShallowestEmpty() 935 // if there are some empty bins and the shallowestEmpty is 936 // smaller than the shallowestUnsaturated then set shallowest 937 // unsaturated to the empty bin. 938 if !noEmptyBins && shallowestEmpty < depth { 939 depth = shallowestEmpty 940 } 941 942 var ( 943 peersCtr = uint(0) 944 candidate = uint8(0) 945 ) 946 _ = peers.EachBin(func(addr swarm.Address, po uint8) (bool, bool, error) { 947 if exclude(addr) { 948 return false, false, nil 949 } 950 peersCtr++ 951 if peersCtr >= uint(k.opt.LowWaterMark) { 952 candidate = po 953 return true, false, nil 954 } 955 return false, false, nil 956 }) 957 958 if depth > candidate { 959 depth = candidate 960 } 961 962 k.depth = depth 963 } 964 965 // connect connects to a peer and gossips its address to our connected peers, 966 // as well as sends the peers we are connected to the newly connected peer 967 func (k *Kad) connect(ctx context.Context, peer swarm.Address, ma ma.Multiaddr) error { 968 k.logger.Debug("attempting connect to peer", "peer_address", peer) 969 970 ctx, cancel := context.WithTimeout(ctx, peerConnectionAttemptTimeout) 971 defer cancel() 972 973 k.metrics.TotalOutboundConnectionAttempts.Inc() 974 975 switch i, err := k.p2p.Connect(ctx, ma); { 976 case errors.Is(err, p2p.ErrNetworkUnavailable): 977 return err 978 case k.p2p.NetworkStatus() == p2p.NetworkStatusUnavailable: 979 return p2p.ErrNetworkUnavailable 980 case errors.Is(err, p2p.ErrDialLightNode): 981 return errPruneEntry 982 case errors.Is(err, p2p.ErrAlreadyConnected): 983 if !i.Overlay.Equal(peer) { 984 return errOverlayMismatch 985 } 986 return nil 987 case errors.Is(err, context.Canceled): 988 return err 989 case errors.Is(err, p2p.ErrPeerBlocklisted): 990 return err 991 case err != nil: 992 k.logger.Debug("could not connect to peer", "peer_address", peer, "error", err) 993 994 retryTime := time.Now().Add(k.opt.TimeToRetry) 995 var e *p2p.ConnectionBackoffError 996 failedAttempts := 0 997 if errors.As(err, &e) { 998 retryTime = e.TryAfter() 999 } else { 1000 failedAttempts = k.waitNext.Attempts(peer) 1001 failedAttempts++ 1002 } 1003 1004 k.metrics.TotalOutboundConnectionFailedAttempts.Inc() 1005 k.collector.Record(peer, im.IncSessionConnectionRetry()) 1006 1007 maxAttempts := maxConnAttempts 1008 if swarm.Proximity(k.base.Bytes(), peer.Bytes()) >= k.neighborhoodDepth() { 1009 maxAttempts = maxNeighborAttempts 1010 } 1011 1012 if failedAttempts >= maxAttempts { 1013 k.waitNext.Remove(peer) 1014 k.knownPeers.Remove(peer) 1015 if err := k.addressBook.Remove(peer); err != nil { 1016 k.logger.Debug("could not remove peer from addressbook", "peer_address", peer) 1017 } 1018 k.logger.Debug("peer pruned from address book", "peer_address", peer) 1019 } else { 1020 k.waitNext.Set(peer, retryTime, failedAttempts) 1021 } 1022 1023 return err 1024 case !i.Overlay.Equal(peer): 1025 _ = k.p2p.Disconnect(peer, errOverlayMismatch.Error()) 1026 _ = k.p2p.Disconnect(i.Overlay, errOverlayMismatch.Error()) 1027 return errOverlayMismatch 1028 } 1029 1030 return k.Announce(ctx, peer, true) 1031 } 1032 1033 // Announce a newly connected peer to our connected peers, but also 1034 // notify the peer about our already connected peers 1035 func (k *Kad) Announce(ctx context.Context, peer swarm.Address, fullnode bool) error { 1036 var addrs []swarm.Address 1037 1038 depth := k.neighborhoodDepth() 1039 isNeighbor := swarm.Proximity(peer.Bytes(), k.base.Bytes()) >= depth 1040 1041 outer: 1042 for bin := uint8(0); bin < swarm.MaxBins; bin++ { 1043 1044 var ( 1045 connectedPeers []swarm.Address 1046 err error 1047 ) 1048 1049 if bin >= depth && isNeighbor { 1050 connectedPeers = k.binPeers(bin, false) // broadcast all neighborhood peers 1051 } else { 1052 connectedPeers, err = randomSubset(k.binPeers(bin, true), k.opt.BroadcastBinSize) 1053 if err != nil { 1054 return err 1055 } 1056 } 1057 1058 for _, connectedPeer := range connectedPeers { 1059 if connectedPeer.Equal(peer) { 1060 continue 1061 } 1062 1063 addrs = append(addrs, connectedPeer) 1064 1065 if !fullnode { 1066 // we continue here so we dont gossip 1067 // about lightnodes to others. 1068 continue 1069 } 1070 // if kademlia is closing, dont enqueue anymore broadcast requests 1071 select { 1072 case <-k.bgBroadcastCtx.Done(): 1073 // we will not interfere with the announce operation by returning here 1074 continue 1075 case <-k.halt: 1076 break outer 1077 default: 1078 } 1079 go func(connectedPeer swarm.Address) { 1080 1081 // Create a new deadline ctx to prevent goroutine pile up 1082 cCtx, cCancel := context.WithTimeout(k.bgBroadcastCtx, time.Minute) 1083 defer cCancel() 1084 1085 if err := k.discovery.BroadcastPeers(cCtx, connectedPeer, peer); err != nil { 1086 k.logger.Debug("peer gossip failed", "new_peer_address", peer, "connected_peer_address", connectedPeer, "error", err) 1087 } 1088 }(connectedPeer) 1089 } 1090 } 1091 1092 if len(addrs) == 0 { 1093 return nil 1094 } 1095 1096 select { 1097 case <-k.halt: 1098 return nil 1099 default: 1100 } 1101 1102 err := k.discovery.BroadcastPeers(ctx, peer, addrs...) 1103 if err != nil { 1104 k.logger.Error(err, "could not broadcast to peer", "peer_address", peer) 1105 _ = k.p2p.Disconnect(peer, "failed broadcasting to peer") 1106 } 1107 1108 return err 1109 } 1110 1111 // AnnounceTo announces a selected peer to another. 1112 func (k *Kad) AnnounceTo(ctx context.Context, addressee, peer swarm.Address, fullnode bool) error { 1113 if !fullnode { 1114 return errAnnounceLightNode 1115 } 1116 1117 return k.discovery.BroadcastPeers(ctx, addressee, peer) 1118 } 1119 1120 // AddPeers adds peers to the knownPeers list. 1121 // This does not guarantee that a connection will immediately 1122 // be made to the peer. 1123 func (k *Kad) AddPeers(addrs ...swarm.Address) { 1124 k.knownPeers.Add(addrs...) 1125 k.notifyManageLoop() 1126 } 1127 1128 func (k *Kad) Pick(peer p2p.Peer) bool { 1129 k.metrics.PickCalls.Inc() 1130 if k.bootnode || !peer.FullNode { 1131 // shortcircuit for bootnode mode AND light node peers - always accept connections, 1132 // at least until we find a better solution. 1133 return true 1134 } 1135 po := swarm.Proximity(k.base.Bytes(), peer.Address.Bytes()) 1136 oversaturated := k.opt.SaturationFunc(po, k.connectedPeers, k.opt.ExcludeFunc(im.Reachability(false))) 1137 // pick the peer if we are not oversaturated 1138 if !oversaturated { 1139 return true 1140 } 1141 k.metrics.PickCallsFalse.Inc() 1142 return false 1143 } 1144 1145 func (k *Kad) binPeers(bin uint8, reachable bool) (peers []swarm.Address) { 1146 1147 _ = k.EachConnectedPeerRev(func(p swarm.Address, po uint8) (bool, bool, error) { 1148 1149 if po == bin { 1150 peers = append(peers, p) 1151 return false, false, nil 1152 } 1153 1154 if po > bin { 1155 return true, false, nil 1156 } 1157 1158 return false, true, nil 1159 1160 }, topology.Select{Reachable: reachable}) 1161 1162 return 1163 } 1164 1165 func isStaticPeer(staticNodes []swarm.Address) func(overlay swarm.Address) bool { 1166 return func(overlay swarm.Address) bool { 1167 return swarm.ContainsAddress(staticNodes, overlay) 1168 } 1169 } 1170 1171 // Connected is called when a peer has dialed in. 1172 // If forceConnection is true `overSaturated` is ignored for non-bootnodes. 1173 func (k *Kad) Connected(ctx context.Context, peer p2p.Peer, forceConnection bool) (err error) { 1174 defer func() { 1175 if err == nil { 1176 k.metrics.TotalInboundConnections.Inc() 1177 k.collector.Record(peer.Address, im.PeerLogIn(time.Now(), im.PeerConnectionDirectionInbound)) 1178 } 1179 }() 1180 1181 address := peer.Address 1182 po := swarm.Proximity(k.base.Bytes(), address.Bytes()) 1183 1184 if overSaturated := k.opt.SaturationFunc(po, k.connectedPeers, k.opt.ExcludeFunc(im.Reachability(false))); overSaturated { 1185 if k.bootnode { 1186 randPeer, err := k.randomPeer(po) 1187 if err != nil { 1188 return fmt.Errorf("failed to get random peer to kick-out: %w", err) 1189 } 1190 _ = k.p2p.Disconnect(randPeer, "kicking out random peer to accommodate node") 1191 return k.onConnected(ctx, address) 1192 } 1193 if !forceConnection { 1194 return topology.ErrOversaturated 1195 } 1196 } 1197 1198 return k.onConnected(ctx, address) 1199 } 1200 1201 func (k *Kad) onConnected(ctx context.Context, addr swarm.Address) error { 1202 if err := k.Announce(ctx, addr, true); err != nil { 1203 return err 1204 } 1205 1206 k.knownPeers.Add(addr) 1207 k.connectedPeers.Add(addr) 1208 1209 k.waitNext.Remove(addr) 1210 1211 k.recalcDepth() 1212 1213 k.notifyManageLoop() 1214 k.notifyPeerSig() 1215 1216 return nil 1217 } 1218 1219 // Disconnected is called when peer disconnects. 1220 func (k *Kad) Disconnected(peer p2p.Peer) { 1221 k.logger.Info("disconnected peer", "peer_address", peer.Address) 1222 1223 k.connectedPeers.Remove(peer.Address) 1224 1225 k.waitNext.SetTryAfter(peer.Address, time.Now().Add(k.opt.TimeToRetry)) 1226 1227 k.metrics.TotalInboundDisconnections.Inc() 1228 k.collector.Record(peer.Address, im.PeerLogOut(time.Now())) 1229 1230 k.recalcDepth() 1231 1232 k.notifyManageLoop() 1233 k.notifyPeerSig() 1234 } 1235 1236 func (k *Kad) notifyPeerSig() { 1237 k.peerSigMtx.Lock() 1238 defer k.peerSigMtx.Unlock() 1239 1240 for _, c := range k.peerSig { 1241 // Every peerSig channel has a buffer capacity of 1, 1242 // so every receiver will get the signal even if the 1243 // select statement has the default case to avoid blocking. 1244 select { 1245 case c <- struct{}{}: 1246 default: 1247 } 1248 } 1249 } 1250 1251 func nClosePeerInSlice(peers []swarm.Address, addr swarm.Address, spf sanctionedPeerFunc, minPO uint8) (swarm.Address, bool) { 1252 for _, peer := range peers { 1253 if spf(peer) { 1254 continue 1255 } 1256 1257 if swarm.ExtendedProximity(peer.Bytes(), addr.Bytes()) >= minPO { 1258 return peer, true 1259 } 1260 } 1261 1262 return swarm.ZeroAddress, false 1263 } 1264 1265 func (k *Kad) IsReachable() bool { 1266 return k.reachability == p2p.ReachabilityStatusPublic 1267 } 1268 1269 // ClosestPeer returns the closest peer to a given address. 1270 func (k *Kad) ClosestPeer(addr swarm.Address, includeSelf bool, filter topology.Select, skipPeers ...swarm.Address) (swarm.Address, error) { 1271 if k.connectedPeers.Length() == 0 { 1272 return swarm.Address{}, topology.ErrNotFound 1273 } 1274 1275 closest := swarm.ZeroAddress 1276 1277 if includeSelf && k.reachability == p2p.ReachabilityStatusPublic { 1278 closest = k.base 1279 } 1280 1281 err := k.EachConnectedPeerRev(func(peer swarm.Address, po uint8) (bool, bool, error) { 1282 if swarm.ContainsAddress(skipPeers, peer) { 1283 return false, false, nil 1284 } 1285 1286 if closest.IsZero() { 1287 closest = peer 1288 return false, false, nil 1289 } 1290 1291 closer, err := peer.Closer(addr, closest) 1292 if closer { 1293 closest = peer 1294 } 1295 if err != nil { 1296 k.logger.Debug("closest peer", "peer", peer, "addr", addr, "error", err) 1297 } 1298 return false, false, nil 1299 }, filter) 1300 1301 if err != nil { 1302 return swarm.Address{}, err 1303 } 1304 1305 if closest.IsZero() { // no peers 1306 return swarm.Address{}, topology.ErrNotFound // only for light nodes 1307 } 1308 1309 // check if self 1310 if closest.Equal(k.base) { 1311 return swarm.Address{}, topology.ErrWantSelf 1312 } 1313 1314 return closest, nil 1315 } 1316 1317 // EachConnectedPeer implements topology.PeerIterator interface. 1318 func (k *Kad) EachConnectedPeer(f topology.EachPeerFunc, filter topology.Select) error { 1319 filters := excludeOps(filter) 1320 return k.connectedPeers.EachBin(func(addr swarm.Address, po uint8) (bool, bool, error) { 1321 if len(filters) > 0 && k.opt.ExcludeFunc(filters...)(addr) { 1322 return false, false, nil 1323 } 1324 return f(addr, po) 1325 }) 1326 } 1327 1328 // EachConnectedPeerRev implements topology.PeerIterator interface. 1329 func (k *Kad) EachConnectedPeerRev(f topology.EachPeerFunc, filter topology.Select) error { 1330 filters := excludeOps(filter) 1331 return k.connectedPeers.EachBinRev(func(addr swarm.Address, po uint8) (bool, bool, error) { 1332 if len(filters) > 0 && k.opt.ExcludeFunc(filters...)(addr) { 1333 return false, false, nil 1334 } 1335 return f(addr, po) 1336 }) 1337 } 1338 1339 // Reachable sets the peer reachability status. 1340 func (k *Kad) Reachable(addr swarm.Address, status p2p.ReachabilityStatus) { 1341 k.collector.Record(addr, im.PeerReachability(status)) 1342 k.logger.Debug("reachability of peer updated", "peer_address", addr, "reachability", status) 1343 if status == p2p.ReachabilityStatusPublic { 1344 k.recalcDepth() 1345 k.notifyManageLoop() 1346 } 1347 } 1348 1349 // UpdateReachability updates node reachability status. 1350 // The status will be updated only once. Updates to status 1351 // p2p.ReachabilityStatusUnknown are ignored. 1352 func (k *Kad) UpdateReachability(status p2p.ReachabilityStatus) { 1353 if status == p2p.ReachabilityStatusUnknown { 1354 return 1355 } 1356 k.logger.Debug("reachability updated", "reachability", status) 1357 k.reachability = status 1358 k.metrics.ReachabilityStatus.WithLabelValues(status.String()).Set(0) 1359 } 1360 1361 // UpdateReachability updates node reachability status. 1362 // The status will be updated only once. Updates to status 1363 // p2p.ReachabilityStatusUnknown are ignored. 1364 func (k *Kad) UpdatePeerHealth(peer swarm.Address, health bool, dur time.Duration) { 1365 k.collector.Record(peer, im.PeerHealth(health), im.PeerLatency(dur)) 1366 } 1367 1368 // SubscribeTopologyChange returns the channel that signals when the connected peers 1369 // set and depth changes. Returned function is safe to be called multiple times. 1370 func (k *Kad) SubscribeTopologyChange() (c <-chan struct{}, unsubscribe func()) { 1371 channel := make(chan struct{}, 1) 1372 var closeOnce sync.Once 1373 1374 k.peerSigMtx.Lock() 1375 defer k.peerSigMtx.Unlock() 1376 1377 k.peerSig = append(k.peerSig, channel) 1378 1379 unsubscribe = func() { 1380 k.peerSigMtx.Lock() 1381 defer k.peerSigMtx.Unlock() 1382 1383 for i, c := range k.peerSig { 1384 if c == channel { 1385 k.peerSig = append(k.peerSig[:i], k.peerSig[i+1:]...) 1386 break 1387 } 1388 } 1389 1390 closeOnce.Do(func() { close(channel) }) 1391 } 1392 1393 return channel, unsubscribe 1394 } 1395 1396 func excludeOps(filter topology.Select) []im.ExcludeOp { 1397 1398 ops := make([]im.ExcludeOp, 0, 2) 1399 1400 if filter.Reachable { 1401 ops = append(ops, im.Reachability(false)) 1402 } 1403 if filter.Healthy { 1404 ops = append(ops, im.Health(false)) 1405 } 1406 1407 return ops 1408 } 1409 1410 // NeighborhoodDepth returns the current Kademlia depth. 1411 func (k *Kad) neighborhoodDepth() uint8 { 1412 k.depthMu.RLock() 1413 defer k.depthMu.RUnlock() 1414 1415 return k.storageRadius 1416 } 1417 1418 func (k *Kad) SetStorageRadius(d uint8) { 1419 1420 k.depthMu.Lock() 1421 defer k.depthMu.Unlock() 1422 1423 if k.storageRadius == d { 1424 return 1425 } 1426 1427 k.storageRadius = d 1428 k.metrics.CurrentStorageDepth.Set(float64(k.storageRadius)) 1429 k.logger.Debug("kademlia set storage radius", "radius", k.storageRadius) 1430 1431 k.notifyManageLoop() 1432 k.notifyPeerSig() 1433 } 1434 1435 func (k *Kad) Snapshot() *topology.KadParams { 1436 var infos []topology.BinInfo 1437 for i := int(swarm.MaxPO); i >= 0; i-- { 1438 infos = append(infos, topology.BinInfo{}) 1439 } 1440 1441 ss := k.collector.Snapshot(time.Now()) 1442 1443 _ = k.connectedPeers.EachBin(func(addr swarm.Address, po uint8) (bool, bool, error) { 1444 infos[po].BinConnected++ 1445 infos[po].ConnectedPeers = append( 1446 infos[po].ConnectedPeers, 1447 &topology.PeerInfo{ 1448 Address: addr, 1449 Metrics: createMetricsSnapshotView(ss[addr.ByteString()]), 1450 }, 1451 ) 1452 return false, false, nil 1453 }) 1454 1455 // output (k.knownPeers ¬ k.connectedPeers) here to not repeat the peers we already have in the connected peers list 1456 _ = k.knownPeers.EachBin(func(addr swarm.Address, po uint8) (bool, bool, error) { 1457 infos[po].BinPopulation++ 1458 1459 for _, v := range infos[po].ConnectedPeers { 1460 // peer already connected, don't show in the known peers list 1461 if v.Address.Equal(addr) { 1462 return false, false, nil 1463 } 1464 } 1465 1466 infos[po].DisconnectedPeers = append( 1467 infos[po].DisconnectedPeers, 1468 &topology.PeerInfo{ 1469 Address: addr, 1470 Metrics: createMetricsSnapshotView(ss[addr.ByteString()]), 1471 }, 1472 ) 1473 return false, false, nil 1474 }) 1475 1476 return &topology.KadParams{ 1477 Base: k.base.String(), 1478 Population: k.knownPeers.Length(), 1479 Connected: k.connectedPeers.Length(), 1480 Timestamp: time.Now(), 1481 NNLowWatermark: k.opt.LowWaterMark, 1482 Depth: k.neighborhoodDepth(), 1483 Reachability: k.reachability.String(), 1484 NetworkAvailability: k.p2p.NetworkStatus().String(), 1485 Bins: topology.KadBins{ 1486 Bin0: infos[0], 1487 Bin1: infos[1], 1488 Bin2: infos[2], 1489 Bin3: infos[3], 1490 Bin4: infos[4], 1491 Bin5: infos[5], 1492 Bin6: infos[6], 1493 Bin7: infos[7], 1494 Bin8: infos[8], 1495 Bin9: infos[9], 1496 Bin10: infos[10], 1497 Bin11: infos[11], 1498 Bin12: infos[12], 1499 Bin13: infos[13], 1500 Bin14: infos[14], 1501 Bin15: infos[15], 1502 Bin16: infos[16], 1503 Bin17: infos[17], 1504 Bin18: infos[18], 1505 Bin19: infos[19], 1506 Bin20: infos[20], 1507 Bin21: infos[21], 1508 Bin22: infos[22], 1509 Bin23: infos[23], 1510 Bin24: infos[24], 1511 Bin25: infos[25], 1512 Bin26: infos[26], 1513 Bin27: infos[27], 1514 Bin28: infos[28], 1515 Bin29: infos[29], 1516 Bin30: infos[30], 1517 Bin31: infos[31], 1518 }, 1519 } 1520 } 1521 1522 // String returns a string represenstation of Kademlia. 1523 func (k *Kad) String() string { 1524 j := k.Snapshot() 1525 b, err := json.MarshalIndent(j, "", " ") 1526 if err != nil { 1527 k.logger.Error(err, "could not marshal kademlia into json") 1528 return "" 1529 } 1530 return string(b) 1531 } 1532 1533 // Halt stops outgoing connections from happening. 1534 // This is needed while we shut down, so that further topology 1535 // changes do not happen while we shut down. 1536 func (k *Kad) Halt() { 1537 close(k.halt) 1538 } 1539 1540 // Close shuts down kademlia. 1541 func (k *Kad) Close() error { 1542 k.logger.Info("kademlia shutting down") 1543 close(k.quit) 1544 cc := make(chan struct{}) 1545 1546 k.bgBroadcastCancel() 1547 1548 go func() { 1549 k.wg.Wait() 1550 close(cc) 1551 }() 1552 1553 eg := errgroup.Group{} 1554 1555 errTimeout := errors.New("timeout") 1556 1557 eg.Go(func() error { 1558 select { 1559 case <-cc: 1560 case <-time.After(peerConnectionAttemptTimeout): 1561 return fmt.Errorf("kademlia shutting down with running goroutines: %w", errTimeout) 1562 } 1563 return nil 1564 }) 1565 1566 eg.Go(func() error { 1567 select { 1568 case <-k.done: 1569 case <-time.After(time.Second * 5): 1570 return fmt.Errorf("kademlia manage loop did not shut down properly: %w", errTimeout) 1571 } 1572 return nil 1573 }) 1574 1575 err := eg.Wait() 1576 1577 k.logger.Info("kademlia persisting peer metrics") 1578 start := time.Now() 1579 if err := k.collector.Finalize(start, false); err != nil { 1580 k.logger.Debug("unable to finalize open sessions", "error", err) 1581 } 1582 k.logger.Debug("metrics collector finalized", "elapsed", time.Since(start)) 1583 1584 return err 1585 } 1586 1587 func randomSubset(addrs []swarm.Address, count int) ([]swarm.Address, error) { 1588 if count >= len(addrs) { 1589 return addrs, nil 1590 } 1591 1592 for i := 0; i < len(addrs); i++ { 1593 b, err := random.Int(random.Reader, big.NewInt(int64(len(addrs)))) 1594 if err != nil { 1595 return nil, err 1596 } 1597 j := int(b.Int64()) 1598 addrs[i], addrs[j] = addrs[j], addrs[i] 1599 } 1600 1601 return addrs[:count], nil 1602 } 1603 1604 func (k *Kad) randomPeer(bin uint8) (swarm.Address, error) { 1605 peers := k.connectedPeers.BinPeers(bin) 1606 1607 for idx := 0; idx < len(peers); { 1608 // do not consider protected peers 1609 if k.staticPeer(peers[idx]) { 1610 peers = append(peers[:idx], peers[idx+1:]...) 1611 continue 1612 } 1613 idx++ 1614 } 1615 1616 if len(peers) == 0 { 1617 return swarm.ZeroAddress, errEmptyBin 1618 } 1619 1620 rndIndx, err := random.Int(random.Reader, big.NewInt(int64(len(peers)))) 1621 if err != nil { 1622 return swarm.ZeroAddress, err 1623 } 1624 1625 return peers[rndIndx.Int64()], nil 1626 } 1627 1628 // createMetricsSnapshotView creates new topology.MetricSnapshotView from the 1629 // given metrics.Snapshot and rounds all the timestamps and durations to its 1630 // nearest second, except for the peer latency, which is given in milliseconds. 1631 func createMetricsSnapshotView(ss *im.Snapshot) *topology.MetricSnapshotView { 1632 if ss == nil { 1633 return nil 1634 } 1635 return &topology.MetricSnapshotView{ 1636 LastSeenTimestamp: time.Unix(0, ss.LastSeenTimestamp).Unix(), 1637 SessionConnectionRetry: ss.SessionConnectionRetry, 1638 ConnectionTotalDuration: ss.ConnectionTotalDuration.Truncate(time.Second).Seconds(), 1639 SessionConnectionDuration: ss.SessionConnectionDuration.Truncate(time.Second).Seconds(), 1640 SessionConnectionDirection: string(ss.SessionConnectionDirection), 1641 LatencyEWMA: ss.LatencyEWMA.Milliseconds(), 1642 Reachability: ss.Reachability.String(), 1643 Healthy: ss.Healthy, 1644 } 1645 }