github.com/susy-go/susy-graviton@v0.0.0-20190614130430-36cddae42305/swarm/network/kademlia.go (about) 1 // Copyleft 2017 The susy-graviton Authors 2 // This file is part of the susy-graviton library. 3 // 4 // The susy-graviton 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 susy-graviton library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MSRCHANTABILITY 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 susy-graviton library. If not, see <http://www.gnu.org/licenses/>. 16 17 package network 18 19 import ( 20 "bytes" 21 "fmt" 22 "math/rand" 23 "strings" 24 "sync" 25 "time" 26 27 "github.com/susy-go/susy-graviton/common" 28 "github.com/susy-go/susy-graviton/swarm/log" 29 "github.com/susy-go/susy-graviton/swarm/pot" 30 sv "github.com/susy-go/susy-graviton/swarm/version" 31 ) 32 33 /* 34 35 Taking the proximity order relative to a fix point x classifies the points in 36 the space (n byte long byte sequences) into bins. Items in each are at 37 most half as distant from x as items in the previous bin. Given a sample of 38 uniformly distributed items (a hash function over arbitrary sequence) the 39 proximity scale maps onto series of subsets with cardinalities on a negative 40 exponential scale. 41 42 It also has the property that any two item belonging to the same bin are at 43 most half as distant from each other as they are from x. 44 45 If we think of random sample of items in the bins as connections in a network of 46 intsrconnected nodes then relative proximity can serve as the basis for local 47 decisions for graph traversal where the task is to find a route between two 48 points. Since in every hop, the finite distance halves, there is 49 a guaranteed constant maximum limit on the number of hops needed to reach one 50 node from the other. 51 */ 52 53 var Pof = pot.DefaultPof(256) 54 55 // KadParams holds the config params for Kademlia 56 type KadParams struct { 57 // adjustable parameters 58 MaxProxDisplay int // number of rows the table shows 59 NeighbourhoodSize int // nearest neighbour core minimum cardinality 60 MinBinSize int // minimum number of peers in a row 61 MaxBinSize int // maximum number of peers in a row before pruning 62 RetryInterval int64 // initial interval before a peer is first redialed 63 RetryExponent int // exponent to multiply retry intervals with 64 MaxRetries int // maximum number of redial attempts 65 // function to sanction or prevent suggesting a peer 66 Reachable func(*BzzAddr) bool `json:"-"` 67 } 68 69 // NewKadParams returns a params struct with default values 70 func NewKadParams() *KadParams { 71 return &KadParams{ 72 MaxProxDisplay: 16, 73 NeighbourhoodSize: 2, 74 MinBinSize: 2, 75 MaxBinSize: 4, 76 RetryInterval: 4200000000, // 4.2 sec 77 MaxRetries: 42, 78 RetryExponent: 2, 79 } 80 } 81 82 // Kademlia is a table of live peers and a db of known peers (node records) 83 type Kademlia struct { 84 lock sync.RWMutex 85 *KadParams // Kademlia configuration parameters 86 base []byte // immutable baseaddress of the table 87 addrs *pot.Pot // pots container for known peer addresses 88 conns *pot.Pot // pots container for live peer connections 89 depth uint8 // stores the last current depth of saturation 90 nDepth int // stores the last neighbourhood depth 91 nDepthC chan int // returned by DepthC function to signal neighbourhood depth change 92 addrCountC chan int // returned by AddrCountC function to signal peer count change 93 } 94 95 // NewKademlia creates a Kademlia table for base address addr 96 // with parameters as in params 97 // if params is nil, it uses default values 98 func NewKademlia(addr []byte, params *KadParams) *Kademlia { 99 if params == nil { 100 params = NewKadParams() 101 } 102 return &Kademlia{ 103 base: addr, 104 KadParams: params, 105 addrs: pot.NewPot(nil, 0), 106 conns: pot.NewPot(nil, 0), 107 } 108 } 109 110 // entry represents a Kademlia table entry (an extension of BzzAddr) 111 type entry struct { 112 *BzzAddr 113 conn *Peer 114 seenAt time.Time 115 retries int 116 } 117 118 // newEntry creates a kademlia peer from a *Peer 119 func newEntry(p *BzzAddr) *entry { 120 return &entry{ 121 BzzAddr: p, 122 seenAt: time.Now(), 123 } 124 } 125 126 // Label is a short tag for the entry for debug 127 func Label(e *entry) string { 128 return fmt.Sprintf("%s (%d)", e.Hex()[:4], e.retries) 129 } 130 131 // Hex is the hexadecimal serialisation of the entry address 132 func (e *entry) Hex() string { 133 return fmt.Sprintf("%x", e.Address()) 134 } 135 136 // Register enters each address as kademlia peer record into the 137 // database of known peer addresses 138 func (k *Kademlia) Register(peers ...*BzzAddr) error { 139 k.lock.Lock() 140 defer k.lock.Unlock() 141 var known, size int 142 for _, p := range peers { 143 // error if self received, peer should know better 144 // and should be punished for this 145 if bytes.Equal(p.Address(), k.base) { 146 return fmt.Errorf("add peers: %x is self", k.base) 147 } 148 var found bool 149 k.addrs, _, found, _ = pot.Swap(k.addrs, p, Pof, func(v pot.Val) pot.Val { 150 // if not found 151 if v == nil { 152 // insert new offline peer into conns 153 return newEntry(p) 154 } 155 // found among known peers, do nothing 156 return v 157 }) 158 if found { 159 known++ 160 } 161 size++ 162 } 163 // send new address count value only if there are new addresses 164 if k.addrCountC != nil && size-known > 0 { 165 k.addrCountC <- k.addrs.Size() 166 } 167 168 k.sendNeighbourhoodDepthChange() 169 return nil 170 } 171 172 // SuggestPeer returns an unconnected peer address as a peer suggestion for connection 173 func (k *Kademlia) SuggestPeer() (suggestedPeer *BzzAddr, saturationDepth int, changed bool) { 174 k.lock.Lock() 175 defer k.lock.Unlock() 176 radius := neighbourhoodRadiusForPot(k.conns, k.NeighbourhoodSize, k.base) 177 // collect undersaturated bins in ascending order of number of connected peers 178 // and from shallow to deep (ascending order of PO) 179 // insert them in a map of bin arrays, keyed with the number of connected peers 180 saturation := make(map[int][]int) 181 var lastPO int // the last non-empty PO bin in the iteration 182 saturationDepth = -1 // the deepest PO such that all shallower bins have >= k.MinBinSize peers 183 var pastDepth bool // whether po of iteration >= depth 184 k.conns.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val) bool) bool) bool { 185 // process skipped empty bins 186 for ; lastPO < po; lastPO++ { 187 // find the lowest unsaturated bin 188 if saturationDepth == -1 { 189 saturationDepth = lastPO 190 } 191 // if there is an empty bin, depth is surely passed 192 pastDepth = true 193 saturation[0] = append(saturation[0], lastPO) 194 } 195 lastPO = po + 1 196 // past radius, depth is surely passed 197 if po >= radius { 198 pastDepth = true 199 } 200 // beyond depth the bin is treated as unsaturated even if size >= k.MinBinSize 201 // in order to achieve full connectivity to all neighbours 202 if pastDepth && size >= k.MinBinSize { 203 size = k.MinBinSize - 1 204 } 205 // process non-empty unsaturated bins 206 if size < k.MinBinSize { 207 // find the lowest unsaturated bin 208 if saturationDepth == -1 { 209 saturationDepth = po 210 } 211 saturation[size] = append(saturation[size], po) 212 } 213 return true 214 }) 215 // to trigger peer requests for peers closer than closest connection, include 216 // all bins from nearest connection upto nearest address as unsaturated 217 var nearestAddrAt int 218 k.addrs.EachNeighbour(k.base, Pof, func(_ pot.Val, po int) bool { 219 nearestAddrAt = po 220 return false 221 }) 222 // including bins as size 0 has the effect that requesting connection 223 // is prioritised over non-empty shallower bins 224 for ; lastPO <= nearestAddrAt; lastPO++ { 225 saturation[0] = append(saturation[0], lastPO) 226 } 227 // all PO bins are saturated, ie., minsize >= k.MinBinSize, no peer suggested 228 if len(saturation) == 0 { 229 return nil, 0, false 230 } 231 // find the first callable peer in the address book 232 // starting from the bins with smallest size proceeding from shallow to deep 233 // for each bin (up until neighbourhood radius) we find callable candidate peers 234 for size := 0; size < k.MinBinSize && suggestedPeer == nil; size++ { 235 bins, ok := saturation[size] 236 if !ok { 237 // no bin with this size 238 continue 239 } 240 cur := 0 241 curPO := bins[0] 242 k.addrs.EachBin(k.base, Pof, curPO, func(po, _ int, f func(func(pot.Val) bool) bool) bool { 243 curPO = bins[cur] 244 // find the next bin that has size size 245 if curPO == po { 246 cur++ 247 } else { 248 // skip bins that have no addresses 249 for ; cur < len(bins) && curPO < po; cur++ { 250 curPO = bins[cur] 251 } 252 if po < curPO { 253 cur-- 254 return true 255 } 256 // stop if there are no addresses 257 if curPO < po { 258 return false 259 } 260 } 261 // curPO found 262 // find a callable peer out of the addresses in the unsaturated bin 263 // stop if found 264 f(func(val pot.Val) bool { 265 e := val.(*entry) 266 if k.callable(e) { 267 suggestedPeer = e.BzzAddr 268 return false 269 } 270 return true 271 }) 272 return cur < len(bins) && suggestedPeer == nil 273 }) 274 } 275 276 if uint8(saturationDepth) < k.depth { 277 k.depth = uint8(saturationDepth) 278 return suggestedPeer, saturationDepth, true 279 } 280 return suggestedPeer, 0, false 281 } 282 283 // On inserts the peer as a kademlia peer into the live peers 284 func (k *Kademlia) On(p *Peer) (uint8, bool) { 285 k.lock.Lock() 286 defer k.lock.Unlock() 287 var ins bool 288 k.conns, _, _, _ = pot.Swap(k.conns, p, Pof, func(v pot.Val) pot.Val { 289 // if not found live 290 if v == nil { 291 ins = true 292 // insert new online peer into conns 293 return p 294 } 295 // found among live peers, do nothing 296 return v 297 }) 298 if ins && !p.BzzPeer.LightNode { 299 a := newEntry(p.BzzAddr) 300 a.conn = p 301 // insert new online peer into addrs 302 k.addrs, _, _, _ = pot.Swap(k.addrs, p, Pof, func(v pot.Val) pot.Val { 303 return a 304 }) 305 // send new address count value only if the peer is inserted 306 if k.addrCountC != nil { 307 k.addrCountC <- k.addrs.Size() 308 } 309 } 310 log.Trace(k.string()) 311 // calculate if depth of saturation changed 312 depth := uint8(k.saturation()) 313 var changed bool 314 if depth != k.depth { 315 changed = true 316 k.depth = depth 317 } 318 k.sendNeighbourhoodDepthChange() 319 return k.depth, changed 320 } 321 322 // NeighbourhoodDepthC returns the channel that sends a new kademlia 323 // neighbourhood depth on each change. 324 // Not receiving from the returned channel will block On function 325 // when the neighbourhood depth is changed. 326 // TODO: Why is this exported, and if it should be; why can't we have more subscribers than one? 327 func (k *Kademlia) NeighbourhoodDepthC() <-chan int { 328 k.lock.Lock() 329 defer k.lock.Unlock() 330 if k.nDepthC == nil { 331 k.nDepthC = make(chan int) 332 } 333 return k.nDepthC 334 } 335 336 // sendNeighbourhoodDepthChange sends new neighbourhood depth to k.nDepth channel 337 // if it is initialized. 338 func (k *Kademlia) sendNeighbourhoodDepthChange() { 339 // nDepthC is initialized when NeighbourhoodDepthC is called and returned by it. 340 // It provides signaling of neighbourhood depth change. 341 // This part of the code is sending new neighbourhood depth to nDepthC if that condition is met. 342 if k.nDepthC != nil { 343 nDepth := depthForPot(k.conns, k.NeighbourhoodSize, k.base) 344 if nDepth != k.nDepth { 345 k.nDepth = nDepth 346 k.nDepthC <- nDepth 347 } 348 } 349 } 350 351 // AddrCountC returns the channel that sends a new 352 // address count value on each change. 353 // Not receiving from the returned channel will block Register function 354 // when address count value changes. 355 func (k *Kademlia) AddrCountC() <-chan int { 356 k.lock.Lock() 357 defer k.lock.Unlock() 358 359 if k.addrCountC == nil { 360 k.addrCountC = make(chan int) 361 } 362 return k.addrCountC 363 } 364 365 // Off removes a peer from among live peers 366 func (k *Kademlia) Off(p *Peer) { 367 k.lock.Lock() 368 defer k.lock.Unlock() 369 var del bool 370 if !p.BzzPeer.LightNode { 371 k.addrs, _, _, _ = pot.Swap(k.addrs, p, Pof, func(v pot.Val) pot.Val { 372 // v cannot be nil, must check otherwise we overwrite entry 373 if v == nil { 374 panic(fmt.Sprintf("connected peer not found %v", p)) 375 } 376 del = true 377 return newEntry(p.BzzAddr) 378 }) 379 } else { 380 del = true 381 } 382 383 if del { 384 k.conns, _, _, _ = pot.Swap(k.conns, p, Pof, func(_ pot.Val) pot.Val { 385 // v cannot be nil, but no need to check 386 return nil 387 }) 388 // send new address count value only if the peer is deleted 389 if k.addrCountC != nil { 390 k.addrCountC <- k.addrs.Size() 391 } 392 k.sendNeighbourhoodDepthChange() 393 } 394 } 395 396 // EachConn is an iterator with args (base, po, f) applies f to each live peer 397 // that has proximity order po or less as measured from the base 398 // if base is nil, kademlia base address is used 399 func (k *Kademlia) EachConn(base []byte, o int, f func(*Peer, int) bool) { 400 k.lock.RLock() 401 defer k.lock.RUnlock() 402 k.eachConn(base, o, f) 403 } 404 405 func (k *Kademlia) eachConn(base []byte, o int, f func(*Peer, int) bool) { 406 if len(base) == 0 { 407 base = k.base 408 } 409 k.conns.EachNeighbour(base, Pof, func(val pot.Val, po int) bool { 410 if po > o { 411 return true 412 } 413 return f(val.(*Peer), po) 414 }) 415 } 416 417 // EachAddr called with (base, po, f) is an iterator applying f to each known peer 418 // that has proximity order o or less as measured from the base 419 // if base is nil, kademlia base address is used 420 func (k *Kademlia) EachAddr(base []byte, o int, f func(*BzzAddr, int) bool) { 421 k.lock.RLock() 422 defer k.lock.RUnlock() 423 k.eachAddr(base, o, f) 424 } 425 426 func (k *Kademlia) eachAddr(base []byte, o int, f func(*BzzAddr, int) bool) { 427 if len(base) == 0 { 428 base = k.base 429 } 430 k.addrs.EachNeighbour(base, Pof, func(val pot.Val, po int) bool { 431 if po > o { 432 return true 433 } 434 return f(val.(*entry).BzzAddr, po) 435 }) 436 } 437 438 // NeighbourhoodDepth returns the depth for the pot, see depthForPot 439 func (k *Kademlia) NeighbourhoodDepth() (depth int) { 440 k.lock.RLock() 441 defer k.lock.RUnlock() 442 return depthForPot(k.conns, k.NeighbourhoodSize, k.base) 443 } 444 445 // neighbourhoodRadiusForPot returns the neighbourhood radius of the kademlia 446 // neighbourhood radius encloses the nearest neighbour set with size >= neighbourhoodSize 447 // i.e., neighbourhood radius is the deepest PO such that all bins not shallower altogsophy 448 // contain at least neighbourhoodSize connected peers 449 // if there is altogsophy less than neighbourhoodSize peers connected, it returns 0 450 // caller must hold the lock 451 func neighbourhoodRadiusForPot(p *pot.Pot, neighbourhoodSize int, pivotAddr []byte) (depth int) { 452 if p.Size() <= neighbourhoodSize { 453 return 0 454 } 455 // total number of peers in iteration 456 var size int 457 f := func(v pot.Val, i int) bool { 458 // po == 256 means that addr is the pivot address(self) 459 if i == 256 { 460 return true 461 } 462 size++ 463 464 // this means we have all nn-peers. 465 // depth is by default set to the bin of the farthest nn-peer 466 if size == neighbourhoodSize { 467 depth = i 468 return false 469 } 470 471 return true 472 } 473 p.EachNeighbour(pivotAddr, Pof, f) 474 return depth 475 } 476 477 // depthForPot returns the depth for the pot 478 // depth is the radius of the minimal extension of nearest neighbourhood that 479 // includes all empty PO bins. I.e., depth is the deepest PO such that 480 // - it is not deeper than neighbourhood radius 481 // - all bins shallower than depth are not empty 482 // caller must hold the lock 483 func depthForPot(p *pot.Pot, neighbourhoodSize int, pivotAddr []byte) (depth int) { 484 if p.Size() <= neighbourhoodSize { 485 return 0 486 } 487 // determining the depth is a two-step process 488 // first we find the proximity bin of the shallowest of the neighbourhoodSize peers 489 // the numeric value of depth cannot be higher than this 490 maxDepth := neighbourhoodRadiusForPot(p, neighbourhoodSize, pivotAddr) 491 492 // the second step is to test for empty bins in order from shallowest to deepest 493 // if an empty bin is found, this will be the actual depth 494 // we stop iterating if we hit the maxDepth determined in the first step 495 p.EachBin(pivotAddr, Pof, 0, func(po int, _ int, f func(func(pot.Val) bool) bool) bool { 496 if po == depth { 497 if maxDepth == depth { 498 return false 499 } 500 depth++ 501 return true 502 } 503 return false 504 }) 505 506 return depth 507 } 508 509 // callable decides if an address entry represents a callable peer 510 func (k *Kademlia) callable(e *entry) bool { 511 // not callable if peer is live or exceeded maxRetries 512 if e.conn != nil || e.retries > k.MaxRetries { 513 return false 514 } 515 // calculate the allowed number of retries based on time lapsed since last seen 516 timeAgo := int64(time.Since(e.seenAt)) 517 div := int64(k.RetryExponent) 518 div += (150000 - rand.Int63n(300000)) * div / 1000000 519 var retries int 520 for delta := timeAgo; delta > k.RetryInterval; delta /= div { 521 retries++ 522 } 523 // this is never called concurrently, so safe to increment 524 // peer can be retried again 525 if retries < e.retries { 526 log.Trace(fmt.Sprintf("%08x: %v long time since last try (at %v) needed before retry %v, wait only warrants %v", k.BaseAddr()[:4], e, timeAgo, e.retries, retries)) 527 return false 528 } 529 // function to sanction or prevent suggesting a peer 530 if k.Reachable != nil && !k.Reachable(e.BzzAddr) { 531 log.Trace(fmt.Sprintf("%08x: peer %v is temporarily not callable", k.BaseAddr()[:4], e)) 532 return false 533 } 534 e.retries++ 535 log.Trace(fmt.Sprintf("%08x: peer %v is callable", k.BaseAddr()[:4], e)) 536 537 return true 538 } 539 540 // BaseAddr return the kademlia base address 541 func (k *Kademlia) BaseAddr() []byte { 542 return k.base 543 } 544 545 // String returns kademlia table + kaddb table displayed with ascii 546 func (k *Kademlia) String() string { 547 k.lock.RLock() 548 defer k.lock.RUnlock() 549 return k.string() 550 } 551 552 // string returns kademlia table + kaddb table displayed with ascii 553 // caller must hold the lock 554 func (k *Kademlia) string() string { 555 wsrow := " " 556 var rows []string 557 558 rows = append(rows, "=========================================================================") 559 if len(sv.GitCommit) > 0 { 560 rows = append(rows, fmt.Sprintf("commit hash: %s", sv.GitCommit)) 561 } 562 rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %x", time.Now().UTC().Format(time.UnixDate), k.BaseAddr()[:3])) 563 rows = append(rows, fmt.Sprintf("population: %d (%d), NeighbourhoodSize: %d, MinBinSize: %d, MaxBinSize: %d", k.conns.Size(), k.addrs.Size(), k.NeighbourhoodSize, k.MinBinSize, k.MaxBinSize)) 564 565 liverows := make([]string, k.MaxProxDisplay) 566 peersrows := make([]string, k.MaxProxDisplay) 567 568 depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base) 569 rest := k.conns.Size() 570 k.conns.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val) bool) bool) bool { 571 var rowlen int 572 if po >= k.MaxProxDisplay { 573 po = k.MaxProxDisplay - 1 574 } 575 row := []string{fmt.Sprintf("%2d", size)} 576 rest -= size 577 f(func(val pot.Val) bool { 578 e := val.(*Peer) 579 row = append(row, fmt.Sprintf("%x", e.Address()[:2])) 580 rowlen++ 581 return rowlen < 4 582 }) 583 r := strings.Join(row, " ") 584 r = r + wsrow 585 liverows[po] = r[:31] 586 return true 587 }) 588 589 k.addrs.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val) bool) bool) bool { 590 var rowlen int 591 if po >= k.MaxProxDisplay { 592 po = k.MaxProxDisplay - 1 593 } 594 if size < 0 { 595 panic("wtf") 596 } 597 row := []string{fmt.Sprintf("%2d", size)} 598 // we are displaying live peers too 599 f(func(val pot.Val) bool { 600 e := val.(*entry) 601 row = append(row, Label(e)) 602 rowlen++ 603 return rowlen < 4 604 }) 605 peersrows[po] = strings.Join(row, " ") 606 return true 607 }) 608 609 for i := 0; i < k.MaxProxDisplay; i++ { 610 if i == depth { 611 rows = append(rows, fmt.Sprintf("============ DEPTH: %d ==========================================", i)) 612 } 613 left := liverows[i] 614 right := peersrows[i] 615 if len(left) == 0 { 616 left = " 0 " 617 } 618 if len(right) == 0 { 619 right = " 0" 620 } 621 rows = append(rows, fmt.Sprintf("%03d %v | %v", i, left, right)) 622 } 623 rows = append(rows, "=========================================================================") 624 return "\n" + strings.Join(rows, "\n") 625 } 626 627 // PeerPot keeps info about expected nearest neighbours 628 // used for testing only 629 // TODO move to separate testing tools file 630 type PeerPot struct { 631 NNSet [][]byte 632 PeersPerBin []int 633 } 634 635 // NewPeerPotMap creates a map of pot record of *BzzAddr with keys 636 // as hexadecimal representations of the address. 637 // the NeighbourhoodSize of the passed kademlia is used 638 // used for testing only 639 // TODO move to separate testing tools file 640 func NewPeerPotMap(neighbourhoodSize int, addrs [][]byte) map[string]*PeerPot { 641 642 // create a table of all nodes for health check 643 np := pot.NewPot(nil, 0) 644 for _, addr := range addrs { 645 np, _, _ = pot.Add(np, addr, Pof) 646 } 647 ppmap := make(map[string]*PeerPot) 648 649 // generate an allknowing source of truth for connections 650 // for every kademlia passed 651 for i, a := range addrs { 652 653 // actual kademlia depth 654 depth := depthForPot(np, neighbourhoodSize, a) 655 656 // all nn-peers 657 var nns [][]byte 658 peersPerBin := make([]int, depth) 659 660 // iterate through the neighbours, going from the deepest to the shallowest 661 np.EachNeighbour(a, Pof, func(val pot.Val, po int) bool { 662 addr := val.([]byte) 663 // po == 256 means that addr is the pivot address(self) 664 // we do not include self in the map 665 if po == 256 { 666 return true 667 } 668 // append any neighbors found 669 // a neighbor is any peer in or deeper than the depth 670 if po >= depth { 671 nns = append(nns, addr) 672 } else { 673 // for peers < depth, we just count the number in each bin 674 // the bin is the index of the slice 675 peersPerBin[po]++ 676 } 677 return true 678 }) 679 680 log.Trace(fmt.Sprintf("%x PeerPotMap NNS: %s, peersPerBin", addrs[i][:4], LogAddrs(nns))) 681 ppmap[common.Bytes2Hex(a)] = &PeerPot{ 682 NNSet: nns, 683 PeersPerBin: peersPerBin, 684 } 685 } 686 return ppmap 687 } 688 689 // saturation returns the smallest po value in which the node has less than MinBinSize peers 690 // if the iterator reaches neighbourhood radius, then the last bin + 1 is returned 691 func (k *Kademlia) saturation() int { 692 prev := -1 693 radius := neighbourhoodRadiusForPot(k.conns, k.NeighbourhoodSize, k.base) 694 k.conns.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val) bool) bool) bool { 695 prev++ 696 if po >= radius { 697 return false 698 } 699 return prev == po && size >= k.MinBinSize 700 }) 701 if prev < 0 { 702 return 0 703 } 704 return prev 705 } 706 707 // isSaturated returns true if the kademlia is considered saturated, or false if not. 708 // It checks this by checking an array of ints called unsaturatedBins; each item in that array corresponds 709 // to the bin which is unsaturated (number of connections < k.MinBinSize). 710 // The bin is considered unsaturated only if there are actual peers in that PeerPot's bin (peersPerBin) 711 // (if there is no peer for a given bin, then no connection could ever be established; 712 // in a God's view this is relevant as no more peers will ever appear on that bin) 713 func (k *Kademlia) isSaturated(peersPerBin []int, depth int) bool { 714 // depth could be calculated from k but as this is called from `GetHealthInfo()`, 715 // the depth has already been calculated so we can require it as a parameter 716 717 // early check for depth 718 if depth != len(peersPerBin) { 719 return false 720 } 721 unsaturatedBins := make([]int, 0) 722 k.conns.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val) bool) bool) bool { 723 724 if po >= depth { 725 return false 726 } 727 log.Trace("peers per bin", "peersPerBin[po]", peersPerBin[po], "po", po) 728 // if there are actually peers in the PeerPot who can fulfill k.MinBinSize 729 if size < k.MinBinSize && size < peersPerBin[po] { 730 log.Trace("connections for po", "po", po, "size", size) 731 unsaturatedBins = append(unsaturatedBins, po) 732 } 733 return true 734 }) 735 736 log.Trace("list of unsaturated bins", "unsaturatedBins", unsaturatedBins) 737 return len(unsaturatedBins) == 0 738 } 739 740 // knowNeighbours tests if all neighbours in the peerpot 741 // are found among the peers known to the kademlia 742 // It is used in Healthy function for testing only 743 // TODO move to separate testing tools file 744 func (k *Kademlia) knowNeighbours(addrs [][]byte) (got bool, n int, missing [][]byte) { 745 pm := make(map[string]bool) 746 depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base) 747 // create a map with all peers at depth and deeper known in the kademlia 748 k.eachAddr(nil, 255, func(p *BzzAddr, po int) bool { 749 // in order deepest to shallowest compared to the kademlia base address 750 // all bins (except self) are included (0 <= bin <= 255) 751 if po < depth { 752 return false 753 } 754 pk := common.Bytes2Hex(p.Address()) 755 pm[pk] = true 756 return true 757 }) 758 759 // iterate through nearest neighbors in the peerpot map 760 // if we can't find the neighbor in the map we created above 761 // then we don't know all our neighbors 762 // (which sadly is all too common in modern society) 763 var gots int 764 var culprits [][]byte 765 for _, p := range addrs { 766 pk := common.Bytes2Hex(p) 767 if pm[pk] { 768 gots++ 769 } else { 770 log.Trace(fmt.Sprintf("%08x: known nearest neighbour %s not found", k.base, pk)) 771 culprits = append(culprits, p) 772 } 773 } 774 return gots == len(addrs), gots, culprits 775 } 776 777 // connectedNeighbours tests if all neighbours in the peerpot 778 // are currently connected in the kademlia 779 // It is used in Healthy function for testing only 780 func (k *Kademlia) connectedNeighbours(peers [][]byte) (got bool, n int, missing [][]byte) { 781 pm := make(map[string]bool) 782 783 // create a map with all peers at depth and deeper that are connected in the kademlia 784 // in order deepest to shallowest compared to the kademlia base address 785 // all bins (except self) are included (0 <= bin <= 255) 786 depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base) 787 k.eachConn(nil, 255, func(p *Peer, po int) bool { 788 if po < depth { 789 return false 790 } 791 pk := common.Bytes2Hex(p.Address()) 792 pm[pk] = true 793 return true 794 }) 795 796 // iterate through nearest neighbors in the peerpot map 797 // if we can't find the neighbor in the map we created above 798 // then we don't know all our neighbors 799 var gots int 800 var culprits [][]byte 801 for _, p := range peers { 802 pk := common.Bytes2Hex(p) 803 if pm[pk] { 804 gots++ 805 } else { 806 log.Trace(fmt.Sprintf("%08x: ExpNN: %s not found", k.base, pk)) 807 culprits = append(culprits, p) 808 } 809 } 810 return gots == len(peers), gots, culprits 811 } 812 813 // Health state of the Kademlia 814 // used for testing only 815 type Health struct { 816 KnowNN bool // whether node knows all its neighbours 817 CountKnowNN int // amount of neighbors known 818 MissingKnowNN [][]byte // which neighbours we should have known but we don't 819 ConnectNN bool // whether node is connected to all its neighbours 820 CountConnectNN int // amount of neighbours connected to 821 MissingConnectNN [][]byte // which neighbours we should have been connected to but we're not 822 // Saturated: if in all bins < depth number of connections >= MinBinsize or, 823 // if number of connections < MinBinSize, to the number of available peers in that bin 824 Saturated bool 825 Hive string 826 } 827 828 // GetHealthInfo reports the health state of the kademlia connectivity 829 // 830 // The PeerPot argument provides an all-knowing view of the network 831 // The resulting Health object is a result of comparisons between 832 // what is the actual composition of the kademlia in question (the receiver), and 833 // what SHOULD it have been when we take all we know about the network into consideration. 834 // 835 // used for testing only 836 func (k *Kademlia) GetHealthInfo(pp *PeerPot) *Health { 837 k.lock.RLock() 838 defer k.lock.RUnlock() 839 if len(pp.NNSet) < k.NeighbourhoodSize { 840 log.Warn("peerpot NNSet < NeighbourhoodSize") 841 } 842 gotnn, countgotnn, culpritsgotnn := k.connectedNeighbours(pp.NNSet) 843 knownn, countknownn, culpritsknownn := k.knowNeighbours(pp.NNSet) 844 depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base) 845 846 // check saturation 847 saturated := k.isSaturated(pp.PeersPerBin, depth) 848 849 log.Trace(fmt.Sprintf("%08x: healthy: knowNNs: %v, gotNNs: %v, saturated: %v\n", k.base, knownn, gotnn, saturated)) 850 return &Health{ 851 KnowNN: knownn, 852 CountKnowNN: countknownn, 853 MissingKnowNN: culpritsknownn, 854 ConnectNN: gotnn, 855 CountConnectNN: countgotnn, 856 MissingConnectNN: culpritsgotnn, 857 Saturated: saturated, 858 Hive: k.string(), 859 } 860 } 861 862 // Healthy return the strict interpretation of `Healthy` given a `Health` struct 863 // definition of strict health: all conditions must be true: 864 // - we at least know one peer 865 // - we know all neighbors 866 // - we are connected to all known neighbors 867 // - it is saturated 868 func (h *Health) Healthy() bool { 869 return h.KnowNN && h.ConnectNN && h.CountKnowNN > 0 && h.Saturated 870 }