github.com/etsc3259/etsc@v0.0.0-20190109113336-a9c2c10f9c95/swarm/network/kademlia.go (about) 1 // Copyright 2017 The go-etsc Authors 2 // This file is part of the go-etsc library. 3 // 4 // The go-etsc library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-etsc library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-etsc 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/ETSC3259/etsc/common" 28 "github.com/ETSC3259/etsc/swarm/log" 29 "github.com/ETSC3259/etsc/swarm/pot" 30 ) 31 32 /* 33 34 Taking the proximity order relative to a fix point x classifies the points in 35 the space (n byte long byte sequences) into bins. Items in each are at 36 most half as distant from x as items in the previous bin. Given a sample of 37 uniformly distributed items (a hash function over arbitrary sequence) the 38 proximity scale maps onto series of subsets with cardinalities on a negative 39 exponential scale. 40 41 It also has the property that any two item belonging to the same bin are at 42 most half as distant from each other as they are from x. 43 44 If we think of random sample of items in the bins as connections in a network of 45 interconnected nodes then relative proximity can serve as the basis for local 46 decisions for graph traversal where the task is to find a route between two 47 points. Since in every hop, the finite distance halves, there is 48 a guaranteed constant maximum limit on the number of hops needed to reach one 49 node from the other. 50 */ 51 52 var pof = pot.DefaultPof(256) 53 54 // KadParams holds the config params for Kademlia 55 type KadParams struct { 56 // adjustable parameters 57 MaxProxDisplay int // number of rows the table shows 58 MinProxBinSize int // nearest neighbour core minimum cardinality 59 MinBinSize int // minimum number of peers in a row 60 MaxBinSize int // maximum number of peers in a row before pruning 61 RetryInterval int64 // initial interval before a peer is first redialed 62 RetryExponent int // exponent to multiply retry intervals with 63 MaxRetries int // maximum number of redial attempts 64 // function to sanction or prevent suggesting a peer 65 Reachable func(*BzzAddr) bool 66 } 67 68 // NewKadParams returns a params struct with default values 69 func NewKadParams() *KadParams { 70 return &KadParams{ 71 MaxProxDisplay: 16, 72 MinProxBinSize: 2, 73 MinBinSize: 2, 74 MaxBinSize: 4, 75 RetryInterval: 4200000000, // 4.2 sec 76 MaxRetries: 42, 77 RetryExponent: 2, 78 } 79 } 80 81 // Kademlia is a table of live peers and a db of known peers (node records) 82 type Kademlia struct { 83 lock sync.RWMutex 84 *KadParams // Kademlia configuration parameters 85 base []byte // immutable baseaddress of the table 86 addrs *pot.Pot // pots container for known peer addresses 87 conns *pot.Pot // pots container for live peer connections 88 depth uint8 // stores the last current depth of saturation 89 nDepth int // stores the last neighbourhood depth 90 nDepthC chan int // returned by DepthC function to signal neighbourhood depth change 91 addrCountC chan int // returned by AddrCountC function to signal peer count change 92 } 93 94 // NewKademlia creates a Kademlia table for base address addr 95 // with parameters as in params 96 // if params is nil, it uses default values 97 func NewKademlia(addr []byte, params *KadParams) *Kademlia { 98 if params == nil { 99 params = NewKadParams() 100 } 101 return &Kademlia{ 102 base: addr, 103 KadParams: params, 104 addrs: pot.NewPot(nil, 0), 105 conns: pot.NewPot(nil, 0), 106 } 107 } 108 109 // entry represents a Kademlia table entry (an extension of BzzAddr) 110 type entry struct { 111 *BzzAddr 112 conn *Peer 113 seenAt time.Time 114 retries int 115 } 116 117 // newEntry creates a kademlia peer from a *Peer 118 func newEntry(p *BzzAddr) *entry { 119 return &entry{ 120 BzzAddr: p, 121 seenAt: time.Now(), 122 } 123 } 124 125 // Label is a short tag for the entry for debug 126 func Label(e *entry) string { 127 return fmt.Sprintf("%s (%d)", e.Hex()[:4], e.retries) 128 } 129 130 // Hex is the hexadecimal serialisation of the entry address 131 func (e *entry) Hex() string { 132 return fmt.Sprintf("%x", e.Address()) 133 } 134 135 // Register enters each address as kademlia peer record into the 136 // database of known peer addresses 137 func (k *Kademlia) Register(peers ...*BzzAddr) error { 138 k.lock.Lock() 139 defer k.lock.Unlock() 140 var known, size int 141 for _, p := range peers { 142 // error if self received, peer should know better 143 // and should be punished for this 144 if bytes.Equal(p.Address(), k.base) { 145 return fmt.Errorf("add peers: %x is self", k.base) 146 } 147 var found bool 148 k.addrs, _, found, _ = pot.Swap(k.addrs, p, pof, func(v pot.Val) pot.Val { 149 // if not found 150 if v == nil { 151 // insert new offline peer into conns 152 return newEntry(p) 153 } 154 // found among known peers, do nothing 155 return v 156 }) 157 if found { 158 known++ 159 } 160 size++ 161 } 162 // send new address count value only if there are new addresses 163 if k.addrCountC != nil && size-known > 0 { 164 k.addrCountC <- k.addrs.Size() 165 } 166 167 k.sendNeighbourhoodDepthChange() 168 return nil 169 } 170 171 // SuggestPeer returns a known peer for the lowest proximity bin for the 172 // lowest bincount below depth 173 // naturally if there is an empty row it returns a peer for that 174 func (k *Kademlia) SuggestPeer() (a *BzzAddr, o int, want bool) { 175 k.lock.Lock() 176 defer k.lock.Unlock() 177 minsize := k.MinBinSize 178 depth := k.neighbourhoodDepth() 179 // if there is a callable neighbour within the current proxBin, connect 180 // this makes sure nearest neighbour set is fully connected 181 var ppo int 182 k.addrs.EachNeighbour(k.base, pof, func(val pot.Val, po int) bool { 183 if po < depth { 184 return false 185 } 186 e := val.(*entry) 187 c := k.callable(e) 188 if c { 189 a = e.BzzAddr 190 } 191 ppo = po 192 return !c 193 }) 194 if a != nil { 195 log.Trace(fmt.Sprintf("%08x candidate nearest neighbour found: %v (%v)", k.BaseAddr()[:4], a, ppo)) 196 return a, 0, false 197 } 198 199 var bpo []int 200 prev := -1 201 k.conns.EachBin(k.base, pof, 0, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool { 202 prev++ 203 for ; prev < po; prev++ { 204 bpo = append(bpo, prev) 205 minsize = 0 206 } 207 if size < minsize { 208 bpo = append(bpo, po) 209 minsize = size 210 } 211 return size > 0 && po < depth 212 }) 213 // all buckets are full, ie., minsize == k.MinBinSize 214 if len(bpo) == 0 { 215 return nil, 0, false 216 } 217 // as long as we got candidate peers to connect to 218 // dont ask for new peers (want = false) 219 // try to select a candidate peer 220 // find the first callable peer 221 nxt := bpo[0] 222 k.addrs.EachBin(k.base, pof, nxt, func(po, _ int, f func(func(pot.Val, int) bool) bool) bool { 223 // for each bin (up until depth) we find callable candidate peers 224 if po >= depth { 225 return false 226 } 227 return f(func(val pot.Val, _ int) bool { 228 e := val.(*entry) 229 c := k.callable(e) 230 if c { 231 a = e.BzzAddr 232 } 233 return !c 234 }) 235 }) 236 // found a candidate 237 if a != nil { 238 return a, 0, false 239 } 240 // no candidate peer found, request for the short bin 241 var changed bool 242 if uint8(nxt) < k.depth { 243 k.depth = uint8(nxt) 244 changed = true 245 } 246 return a, nxt, changed 247 } 248 249 // On inserts the peer as a kademlia peer into the live peers 250 func (k *Kademlia) On(p *Peer) (uint8, bool) { 251 k.lock.Lock() 252 defer k.lock.Unlock() 253 var ins bool 254 k.conns, _, _, _ = pot.Swap(k.conns, p, pof, func(v pot.Val) pot.Val { 255 // if not found live 256 if v == nil { 257 ins = true 258 // insert new online peer into conns 259 return p 260 } 261 // found among live peers, do nothing 262 return v 263 }) 264 if ins && !p.BzzPeer.LightNode { 265 a := newEntry(p.BzzAddr) 266 a.conn = p 267 // insert new online peer into addrs 268 k.addrs, _, _, _ = pot.Swap(k.addrs, p, pof, func(v pot.Val) pot.Val { 269 return a 270 }) 271 // send new address count value only if the peer is inserted 272 if k.addrCountC != nil { 273 k.addrCountC <- k.addrs.Size() 274 } 275 } 276 log.Trace(k.string()) 277 // calculate if depth of saturation changed 278 depth := uint8(k.saturation(k.MinBinSize)) 279 var changed bool 280 if depth != k.depth { 281 changed = true 282 k.depth = depth 283 } 284 k.sendNeighbourhoodDepthChange() 285 return k.depth, changed 286 } 287 288 // NeighbourhoodDepthC returns the channel that sends a new kademlia 289 // neighbourhood depth on each change. 290 // Not receiving from the returned channel will block On function 291 // when the neighbourhood depth is changed. 292 func (k *Kademlia) NeighbourhoodDepthC() <-chan int { 293 k.lock.Lock() 294 defer k.lock.Unlock() 295 if k.nDepthC == nil { 296 k.nDepthC = make(chan int) 297 } 298 return k.nDepthC 299 } 300 301 // sendNeighbourhoodDepthChange sends new neighbourhood depth to k.nDepth channel 302 // if it is initialized. 303 func (k *Kademlia) sendNeighbourhoodDepthChange() { 304 // nDepthC is initialized when NeighbourhoodDepthC is called and returned by it. 305 // It provides signaling of neighbourhood depth change. 306 // This part of the code is sending new neighbourhood depth to nDepthC if that condition is met. 307 if k.nDepthC != nil { 308 nDepth := k.neighbourhoodDepth() 309 if nDepth != k.nDepth { 310 k.nDepth = nDepth 311 k.nDepthC <- nDepth 312 } 313 } 314 } 315 316 // AddrCountC returns the channel that sends a new 317 // address count value on each change. 318 // Not receiving from the returned channel will block Register function 319 // when address count value changes. 320 func (k *Kademlia) AddrCountC() <-chan int { 321 if k.addrCountC == nil { 322 k.addrCountC = make(chan int) 323 } 324 return k.addrCountC 325 } 326 327 // Off removes a peer from among live peers 328 func (k *Kademlia) Off(p *Peer) { 329 k.lock.Lock() 330 defer k.lock.Unlock() 331 var del bool 332 if !p.BzzPeer.LightNode { 333 k.addrs, _, _, _ = pot.Swap(k.addrs, p, pof, func(v pot.Val) pot.Val { 334 // v cannot be nil, must check otherwise we overwrite entry 335 if v == nil { 336 panic(fmt.Sprintf("connected peer not found %v", p)) 337 } 338 del = true 339 return newEntry(p.BzzAddr) 340 }) 341 } else { 342 del = true 343 } 344 345 if del { 346 k.conns, _, _, _ = pot.Swap(k.conns, p, pof, func(_ pot.Val) pot.Val { 347 // v cannot be nil, but no need to check 348 return nil 349 }) 350 // send new address count value only if the peer is deleted 351 if k.addrCountC != nil { 352 k.addrCountC <- k.addrs.Size() 353 } 354 k.sendNeighbourhoodDepthChange() 355 } 356 } 357 358 func (k *Kademlia) EachBin(base []byte, pof pot.Pof, o int, eachBinFunc func(conn *Peer, po int) bool) { 359 k.lock.RLock() 360 defer k.lock.RUnlock() 361 362 var startPo int 363 var endPo int 364 kadDepth := k.neighbourhoodDepth() 365 366 k.conns.EachBin(base, pof, o, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool { 367 if startPo > 0 && endPo != k.MaxProxDisplay { 368 startPo = endPo + 1 369 } 370 if po < kadDepth { 371 endPo = po 372 } else { 373 endPo = k.MaxProxDisplay 374 } 375 376 for bin := startPo; bin <= endPo; bin++ { 377 f(func(val pot.Val, _ int) bool { 378 return eachBinFunc(val.(*Peer), bin) 379 }) 380 } 381 return true 382 }) 383 } 384 385 // EachConn is an iterator with args (base, po, f) applies f to each live peer 386 // that has proximity order po or less as measured from the base 387 // if base is nil, kademlia base address is used 388 func (k *Kademlia) EachConn(base []byte, o int, f func(*Peer, int, bool) bool) { 389 k.lock.RLock() 390 defer k.lock.RUnlock() 391 k.eachConn(base, o, f) 392 } 393 394 func (k *Kademlia) eachConn(base []byte, o int, f func(*Peer, int, bool) bool) { 395 if len(base) == 0 { 396 base = k.base 397 } 398 depth := k.neighbourhoodDepth() 399 k.conns.EachNeighbour(base, pof, func(val pot.Val, po int) bool { 400 if po > o { 401 return true 402 } 403 return f(val.(*Peer), po, po >= depth) 404 }) 405 } 406 407 // EachAddr called with (base, po, f) is an iterator applying f to each known peer 408 // that has proximity order po or less as measured from the base 409 // if base is nil, kademlia base address is used 410 func (k *Kademlia) EachAddr(base []byte, o int, f func(*BzzAddr, int, bool) bool) { 411 k.lock.RLock() 412 defer k.lock.RUnlock() 413 k.eachAddr(base, o, f) 414 } 415 416 func (k *Kademlia) eachAddr(base []byte, o int, f func(*BzzAddr, int, bool) bool) { 417 if len(base) == 0 { 418 base = k.base 419 } 420 depth := k.neighbourhoodDepth() 421 k.addrs.EachNeighbour(base, pof, func(val pot.Val, po int) bool { 422 if po > o { 423 return true 424 } 425 return f(val.(*entry).BzzAddr, po, po >= depth) 426 }) 427 } 428 429 // neighbourhoodDepth returns the proximity order that defines the distance of 430 // the nearest neighbour set with cardinality >= MinProxBinSize 431 // if there is altogether less than MinProxBinSize peers it returns 0 432 // caller must hold the lock 433 func (k *Kademlia) neighbourhoodDepth() (depth int) { 434 if k.conns.Size() < k.MinProxBinSize { 435 return 0 436 } 437 var size int 438 f := func(v pot.Val, i int) bool { 439 size++ 440 depth = i 441 return size < k.MinProxBinSize 442 } 443 k.conns.EachNeighbour(k.base, pof, f) 444 return depth 445 } 446 447 // callable decides if an address entry represents a callable peer 448 func (k *Kademlia) callable(e *entry) bool { 449 // not callable if peer is live or exceeded maxRetries 450 if e.conn != nil || e.retries > k.MaxRetries { 451 return false 452 } 453 // calculate the allowed number of retries based on time lapsed since last seen 454 timeAgo := int64(time.Since(e.seenAt)) 455 div := int64(k.RetryExponent) 456 div += (150000 - rand.Int63n(300000)) * div / 1000000 457 var retries int 458 for delta := timeAgo; delta > k.RetryInterval; delta /= div { 459 retries++ 460 } 461 // this is never called concurrently, so safe to increment 462 // peer can be retried again 463 if retries < e.retries { 464 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)) 465 return false 466 } 467 // function to sanction or prevent suggesting a peer 468 if k.Reachable != nil && !k.Reachable(e.BzzAddr) { 469 log.Trace(fmt.Sprintf("%08x: peer %v is temporarily not callable", k.BaseAddr()[:4], e)) 470 return false 471 } 472 e.retries++ 473 log.Trace(fmt.Sprintf("%08x: peer %v is callable", k.BaseAddr()[:4], e)) 474 475 return true 476 } 477 478 // BaseAddr return the kademlia base address 479 func (k *Kademlia) BaseAddr() []byte { 480 return k.base 481 } 482 483 // String returns kademlia table + kaddb table displayed with ascii 484 func (k *Kademlia) String() string { 485 k.lock.RLock() 486 defer k.lock.RUnlock() 487 return k.string() 488 } 489 490 // string returns kademlia table + kaddb table displayed with ascii 491 // caller must hold the lock 492 func (k *Kademlia) string() string { 493 wsrow := " " 494 var rows []string 495 496 rows = append(rows, "=========================================================================") 497 rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %x", time.Now().UTC().Format(time.UnixDate), k.BaseAddr()[:3])) 498 rows = append(rows, fmt.Sprintf("population: %d (%d), MinProxBinSize: %d, MinBinSize: %d, MaxBinSize: %d", k.conns.Size(), k.addrs.Size(), k.MinProxBinSize, k.MinBinSize, k.MaxBinSize)) 499 500 liverows := make([]string, k.MaxProxDisplay) 501 peersrows := make([]string, k.MaxProxDisplay) 502 503 depth := k.neighbourhoodDepth() 504 rest := k.conns.Size() 505 k.conns.EachBin(k.base, pof, 0, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool { 506 var rowlen int 507 if po >= k.MaxProxDisplay { 508 po = k.MaxProxDisplay - 1 509 } 510 row := []string{fmt.Sprintf("%2d", size)} 511 rest -= size 512 f(func(val pot.Val, vpo int) bool { 513 e := val.(*Peer) 514 row = append(row, fmt.Sprintf("%x", e.Address()[:2])) 515 rowlen++ 516 return rowlen < 4 517 }) 518 r := strings.Join(row, " ") 519 r = r + wsrow 520 liverows[po] = r[:31] 521 return true 522 }) 523 524 k.addrs.EachBin(k.base, pof, 0, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool { 525 var rowlen int 526 if po >= k.MaxProxDisplay { 527 po = k.MaxProxDisplay - 1 528 } 529 if size < 0 { 530 panic("wtf") 531 } 532 row := []string{fmt.Sprintf("%2d", size)} 533 // we are displaying live peers too 534 f(func(val pot.Val, vpo int) bool { 535 e := val.(*entry) 536 row = append(row, Label(e)) 537 rowlen++ 538 return rowlen < 4 539 }) 540 peersrows[po] = strings.Join(row, " ") 541 return true 542 }) 543 544 for i := 0; i < k.MaxProxDisplay; i++ { 545 if i == depth { 546 rows = append(rows, fmt.Sprintf("============ DEPTH: %d ==========================================", i)) 547 } 548 left := liverows[i] 549 right := peersrows[i] 550 if len(left) == 0 { 551 left = " 0 " 552 } 553 if len(right) == 0 { 554 right = " 0" 555 } 556 rows = append(rows, fmt.Sprintf("%03d %v | %v", i, left, right)) 557 } 558 rows = append(rows, "=========================================================================") 559 return "\n" + strings.Join(rows, "\n") 560 } 561 562 // PeerPot keeps info about expected nearest neighbours and empty bins 563 // used for testing only 564 type PeerPot struct { 565 NNSet [][]byte 566 EmptyBins []int 567 } 568 569 // NewPeerPotMap creates a map of pot record of *BzzAddr with keys 570 // as hexadecimal representations of the address. 571 // used for testing only 572 func NewPeerPotMap(kadMinProxSize int, addrs [][]byte) map[string]*PeerPot { 573 // create a table of all nodes for health check 574 np := pot.NewPot(nil, 0) 575 for _, addr := range addrs { 576 np, _, _ = pot.Add(np, addr, pof) 577 } 578 ppmap := make(map[string]*PeerPot) 579 580 for i, a := range addrs { 581 pl := 256 582 prev := 256 583 var emptyBins []int 584 var nns [][]byte 585 np.EachNeighbour(addrs[i], pof, func(val pot.Val, po int) bool { 586 a := val.([]byte) 587 if po == 256 { 588 return true 589 } 590 if pl == 256 || pl == po { 591 nns = append(nns, a) 592 } 593 if pl == 256 && len(nns) >= kadMinProxSize { 594 pl = po 595 prev = po 596 } 597 if prev < pl { 598 for j := prev; j > po; j-- { 599 emptyBins = append(emptyBins, j) 600 } 601 } 602 prev = po - 1 603 return true 604 }) 605 for j := prev; j >= 0; j-- { 606 emptyBins = append(emptyBins, j) 607 } 608 log.Trace(fmt.Sprintf("%x NNS: %s", addrs[i][:4], LogAddrs(nns))) 609 ppmap[common.Bytes2Hex(a)] = &PeerPot{nns, emptyBins} 610 } 611 return ppmap 612 } 613 614 // saturation returns the lowest proximity order that the bin for that order 615 // has less than n peers 616 // It is used in Healthy function for testing only 617 func (k *Kademlia) saturation(n int) int { 618 prev := -1 619 k.addrs.EachBin(k.base, pof, 0, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool { 620 prev++ 621 return prev == po && size >= n 622 }) 623 depth := k.neighbourhoodDepth() 624 if depth < prev { 625 return depth 626 } 627 return prev 628 } 629 630 // full returns true if all required bins have connected peers. 631 // It is used in Healthy function for testing only 632 func (k *Kademlia) full(emptyBins []int) (full bool) { 633 prev := 0 634 e := len(emptyBins) 635 ok := true 636 depth := k.neighbourhoodDepth() 637 k.conns.EachBin(k.base, pof, 0, func(po, _ int, _ func(func(val pot.Val, i int) bool) bool) bool { 638 if prev == depth+1 { 639 return true 640 } 641 for i := prev; i < po; i++ { 642 e-- 643 if e < 0 { 644 ok = false 645 return false 646 } 647 if emptyBins[e] != i { 648 log.Trace(fmt.Sprintf("%08x po: %d, i: %d, e: %d, emptybins: %v", k.BaseAddr()[:4], po, i, e, logEmptyBins(emptyBins))) 649 if emptyBins[e] < i { 650 panic("incorrect peerpot") 651 } 652 ok = false 653 return false 654 } 655 } 656 prev = po + 1 657 return true 658 }) 659 if !ok { 660 return false 661 } 662 return e == 0 663 } 664 665 // knowNearestNeighbours tests if all known nearest neighbours given as arguments 666 // are found in the addressbook 667 // It is used in Healthy function for testing only 668 func (k *Kademlia) knowNearestNeighbours(peers [][]byte) bool { 669 pm := make(map[string]bool) 670 671 k.eachAddr(nil, 255, func(p *BzzAddr, po int, nn bool) bool { 672 if !nn { 673 return false 674 } 675 pk := fmt.Sprintf("%x", p.Address()) 676 pm[pk] = true 677 return true 678 }) 679 for _, p := range peers { 680 pk := fmt.Sprintf("%x", p) 681 if !pm[pk] { 682 log.Trace(fmt.Sprintf("%08x: known nearest neighbour %s not found", k.BaseAddr()[:4], pk[:8])) 683 return false 684 } 685 } 686 return true 687 } 688 689 // gotNearestNeighbours tests if all known nearest neighbours given as arguments 690 // are connected peers 691 // It is used in Healthy function for testing only 692 func (k *Kademlia) gotNearestNeighbours(peers [][]byte) (got bool, n int, missing [][]byte) { 693 pm := make(map[string]bool) 694 695 k.eachConn(nil, 255, func(p *Peer, po int, nn bool) bool { 696 if !nn { 697 return false 698 } 699 pk := fmt.Sprintf("%x", p.Address()) 700 pm[pk] = true 701 return true 702 }) 703 var gots int 704 var culprits [][]byte 705 for _, p := range peers { 706 pk := fmt.Sprintf("%x", p) 707 if pm[pk] { 708 gots++ 709 } else { 710 log.Trace(fmt.Sprintf("%08x: ExpNN: %s not found", k.BaseAddr()[:4], pk[:8])) 711 culprits = append(culprits, p) 712 } 713 } 714 return gots == len(peers), gots, culprits 715 } 716 717 // Health state of the Kademlia 718 // used for testing only 719 type Health struct { 720 KnowNN bool // whether node knows all its nearest neighbours 721 GotNN bool // whether node is connected to all its nearest neighbours 722 CountNN int // amount of nearest neighbors connected to 723 CulpritsNN [][]byte // which known NNs are missing 724 Full bool // whether node has a peer in each kademlia bin (where there is such a peer) 725 Hive string 726 } 727 728 // Healthy reports the health state of the kademlia connectivity 729 // returns a Health struct 730 // used for testing only 731 func (k *Kademlia) Healthy(pp *PeerPot) *Health { 732 k.lock.RLock() 733 defer k.lock.RUnlock() 734 gotnn, countnn, culpritsnn := k.gotNearestNeighbours(pp.NNSet) 735 knownn := k.knowNearestNeighbours(pp.NNSet) 736 full := k.full(pp.EmptyBins) 737 log.Trace(fmt.Sprintf("%08x: healthy: knowNNs: %v, gotNNs: %v, full: %v\n", k.BaseAddr()[:4], knownn, gotnn, full)) 738 return &Health{knownn, gotnn, countnn, culpritsnn, full, k.string()} 739 } 740 741 func logEmptyBins(ebs []int) string { 742 var ebss []string 743 for _, eb := range ebs { 744 ebss = append(ebss, fmt.Sprintf("%d", eb)) 745 } 746 return strings.Join(ebss, ", ") 747 }