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  }