github.com/shyftnetwork/go-empyrean@v1.8.3-0.20191127201940-fbfca9338f04/swarm/network/kademlia/kademlia.go (about)

     1  // Copyright 2016 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum 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-ethereum 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-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package kademlia
    18  
    19  import (
    20  	"fmt"
    21  	"sort"
    22  	"strings"
    23  	"sync"
    24  	"time"
    25  
    26  	"github.com/ShyftNetwork/go-empyrean/log"
    27  	"github.com/ShyftNetwork/go-empyrean/metrics"
    28  )
    29  
    30  //metrics variables
    31  //For metrics, we want to count how many times peers are added/removed
    32  //at a certain index. Thus we do that with an array of counters with
    33  //entry for each index
    34  var (
    35  	bucketAddIndexCount []metrics.Counter
    36  	bucketRmIndexCount  []metrics.Counter
    37  )
    38  
    39  const (
    40  	bucketSize   = 4
    41  	proxBinSize  = 2
    42  	maxProx      = 8
    43  	connRetryExp = 2
    44  	maxPeers     = 100
    45  )
    46  
    47  var (
    48  	purgeInterval        = 42 * time.Hour
    49  	initialRetryInterval = 42 * time.Millisecond
    50  	maxIdleInterval      = 42 * 1000 * time.Millisecond
    51  	// maxIdleInterval      = 42 * 10	0 * time.Millisecond
    52  )
    53  
    54  type KadParams struct {
    55  	// adjustable parameters
    56  	MaxProx              int
    57  	ProxBinSize          int
    58  	BucketSize           int
    59  	PurgeInterval        time.Duration
    60  	InitialRetryInterval time.Duration
    61  	MaxIdleInterval      time.Duration
    62  	ConnRetryExp         int
    63  }
    64  
    65  func NewDefaultKadParams() *KadParams {
    66  	return &KadParams{
    67  		MaxProx:              maxProx,
    68  		ProxBinSize:          proxBinSize,
    69  		BucketSize:           bucketSize,
    70  		PurgeInterval:        purgeInterval,
    71  		InitialRetryInterval: initialRetryInterval,
    72  		MaxIdleInterval:      maxIdleInterval,
    73  		ConnRetryExp:         connRetryExp,
    74  	}
    75  }
    76  
    77  // Kademlia is a table of active nodes
    78  type Kademlia struct {
    79  	addr       Address      // immutable baseaddress of the table
    80  	*KadParams              // Kademlia configuration parameters
    81  	proxLimit  int          // state, the PO of the first row of the most proximate bin
    82  	proxSize   int          // state, the number of peers in the most proximate bin
    83  	count      int          // number of active peers (w live connection)
    84  	buckets    [][]Node     // the actual bins
    85  	db         *KadDb       // kaddb, node record database
    86  	lock       sync.RWMutex // mutex to access buckets
    87  }
    88  
    89  type Node interface {
    90  	Addr() Address
    91  	Url() string
    92  	LastActive() time.Time
    93  	Drop()
    94  }
    95  
    96  // public constructor
    97  // add is the base address of the table
    98  // params is KadParams configuration
    99  func New(addr Address, params *KadParams) *Kademlia {
   100  	buckets := make([][]Node, params.MaxProx+1)
   101  	kad := &Kademlia{
   102  		addr:      addr,
   103  		KadParams: params,
   104  		buckets:   buckets,
   105  		db:        newKadDb(addr, params),
   106  	}
   107  	kad.initMetricsVariables()
   108  	return kad
   109  }
   110  
   111  // accessor for KAD base address
   112  func (self *Kademlia) Addr() Address {
   113  	return self.addr
   114  }
   115  
   116  // accessor for KAD active node count
   117  func (self *Kademlia) Count() int {
   118  	defer self.lock.Unlock()
   119  	self.lock.Lock()
   120  	return self.count
   121  }
   122  
   123  // accessor for KAD active node count
   124  func (self *Kademlia) DBCount() int {
   125  	return self.db.count()
   126  }
   127  
   128  // On is the entry point called when a new nodes is added
   129  // unsafe in that node is not checked to be already active node (to be called once)
   130  func (self *Kademlia) On(node Node, cb func(*NodeRecord, Node) error) (err error) {
   131  	log.Debug(fmt.Sprintf("%v", self))
   132  	defer self.lock.Unlock()
   133  	self.lock.Lock()
   134  
   135  	index := self.proximityBin(node.Addr())
   136  	record := self.db.findOrCreate(index, node.Addr(), node.Url())
   137  
   138  	if cb != nil {
   139  		err = cb(record, node)
   140  		log.Trace(fmt.Sprintf("cb(%v, %v) ->%v", record, node, err))
   141  		if err != nil {
   142  			return fmt.Errorf("unable to add node %v, callback error: %v", node.Addr(), err)
   143  		}
   144  		log.Debug(fmt.Sprintf("add node record %v with node %v", record, node))
   145  	}
   146  
   147  	// insert in kademlia table of active nodes
   148  	bucket := self.buckets[index]
   149  	// if bucket is full insertion replaces the worst node
   150  	// TODO: give priority to peers with active traffic
   151  	if len(bucket) < self.BucketSize { // >= allows us to add peers beyond the bucketsize limitation
   152  		self.buckets[index] = append(bucket, node)
   153  		bucketAddIndexCount[index].Inc(1)
   154  		log.Debug(fmt.Sprintf("add node %v to table", node))
   155  		self.setProxLimit(index, true)
   156  		record.node = node
   157  		self.count++
   158  		return nil
   159  	}
   160  
   161  	// always rotate peers
   162  	idle := self.MaxIdleInterval
   163  	var pos int
   164  	var replaced Node
   165  	for i, p := range bucket {
   166  		idleInt := time.Since(p.LastActive())
   167  		if idleInt > idle {
   168  			idle = idleInt
   169  			pos = i
   170  			replaced = p
   171  		}
   172  	}
   173  	if replaced == nil {
   174  		log.Debug(fmt.Sprintf("all peers wanted, PO%03d bucket full", index))
   175  		return fmt.Errorf("bucket full")
   176  	}
   177  	log.Debug(fmt.Sprintf("node %v replaced by %v (idle for %v  > %v)", replaced, node, idle, self.MaxIdleInterval))
   178  	replaced.Drop()
   179  	// actually replace in the row. When off(node) is called, the peer is no longer in the row
   180  	bucket[pos] = node
   181  	// there is no change in bucket cardinalities so no prox limit adjustment is needed
   182  	record.node = node
   183  	self.count++
   184  	return nil
   185  
   186  }
   187  
   188  // Off is the called when a node is taken offline (from the protocol main loop exit)
   189  func (self *Kademlia) Off(node Node, cb func(*NodeRecord, Node)) (err error) {
   190  	self.lock.Lock()
   191  	defer self.lock.Unlock()
   192  
   193  	index := self.proximityBin(node.Addr())
   194  	bucketRmIndexCount[index].Inc(1)
   195  	bucket := self.buckets[index]
   196  	for i := 0; i < len(bucket); i++ {
   197  		if node.Addr() == bucket[i].Addr() {
   198  			self.buckets[index] = append(bucket[:i], bucket[(i+1):]...)
   199  			self.setProxLimit(index, false)
   200  			break
   201  		}
   202  	}
   203  
   204  	record := self.db.index[node.Addr()]
   205  	// callback on remove
   206  	if cb != nil {
   207  		cb(record, record.node)
   208  	}
   209  	record.node = nil
   210  	self.count--
   211  	log.Debug(fmt.Sprintf("remove node %v from table, population now is %v", node, self.count))
   212  
   213  	return
   214  }
   215  
   216  // proxLimit is dynamically adjusted so that
   217  // 1) there is no empty buckets in bin < proxLimit and
   218  // 2) the sum of all items are the minimum possible but higher than ProxBinSize
   219  // adjust Prox (proxLimit and proxSize after an insertion/removal of nodes)
   220  // caller holds the lock
   221  func (self *Kademlia) setProxLimit(r int, on bool) {
   222  	// if the change is outside the core (PO lower)
   223  	// and the change does not leave a bucket empty then
   224  	// no adjustment needed
   225  	if r < self.proxLimit && len(self.buckets[r]) > 0 {
   226  		return
   227  	}
   228  	// if on=a node was added, then r must be within prox limit so increment cardinality
   229  	if on {
   230  		self.proxSize++
   231  		curr := len(self.buckets[self.proxLimit])
   232  		// if now core is big enough without the furthest bucket, then contract
   233  		// this can result in more than one bucket change
   234  		for self.proxSize >= self.ProxBinSize+curr && curr > 0 {
   235  			self.proxSize -= curr
   236  			self.proxLimit++
   237  			curr = len(self.buckets[self.proxLimit])
   238  
   239  			log.Trace(fmt.Sprintf("proxbin contraction (size: %v, limit: %v, bin: %v)", self.proxSize, self.proxLimit, r))
   240  		}
   241  		return
   242  	}
   243  	// otherwise
   244  	if r >= self.proxLimit {
   245  		self.proxSize--
   246  	}
   247  	// expand core by lowering prox limit until hit zero or cover the empty bucket or reached target cardinality
   248  	for (self.proxSize < self.ProxBinSize || r < self.proxLimit) &&
   249  		self.proxLimit > 0 {
   250  		//
   251  		self.proxLimit--
   252  		self.proxSize += len(self.buckets[self.proxLimit])
   253  		log.Trace(fmt.Sprintf("proxbin expansion (size: %v, limit: %v, bin: %v)", self.proxSize, self.proxLimit, r))
   254  	}
   255  }
   256  
   257  /*
   258  returns the list of nodes belonging to the same proximity bin
   259  as the target. The most proximate bin will be the union of the bins between
   260  proxLimit and MaxProx.
   261  */
   262  func (self *Kademlia) FindClosest(target Address, max int) []Node {
   263  	self.lock.Lock()
   264  	defer self.lock.Unlock()
   265  
   266  	r := nodesByDistance{
   267  		target: target,
   268  	}
   269  
   270  	po := self.proximityBin(target)
   271  	index := po
   272  	step := 1
   273  	log.Trace(fmt.Sprintf("serving %v nodes at %v (PO%02d)", max, index, po))
   274  
   275  	// if max is set to 0, just want a full bucket, dynamic number
   276  	min := max
   277  	// set limit to max
   278  	limit := max
   279  	if max == 0 {
   280  		min = 1
   281  		limit = maxPeers
   282  	}
   283  
   284  	var n int
   285  	for index >= 0 {
   286  		// add entire bucket
   287  		for _, p := range self.buckets[index] {
   288  			r.push(p, limit)
   289  			n++
   290  		}
   291  		// terminate if index reached the bottom or enough peers > min
   292  		log.Trace(fmt.Sprintf("add %v -> %v (PO%02d, PO%03d)", len(self.buckets[index]), n, index, po))
   293  		if n >= min && (step < 0 || max == 0) {
   294  			break
   295  		}
   296  		// reach top most non-empty PO bucket, turn around
   297  		if index == self.MaxProx {
   298  			index = po
   299  			step = -1
   300  		}
   301  		index += step
   302  	}
   303  	log.Trace(fmt.Sprintf("serve %d (<=%d) nodes for target lookup %v (PO%03d)", n, max, target, po))
   304  	return r.nodes
   305  }
   306  
   307  func (self *Kademlia) Suggest() (*NodeRecord, bool, int) {
   308  	defer self.lock.RUnlock()
   309  	self.lock.RLock()
   310  	return self.db.findBest(self.BucketSize, func(i int) int { return len(self.buckets[i]) })
   311  }
   312  
   313  //  adds node records to kaddb (persisted node record db)
   314  func (self *Kademlia) Add(nrs []*NodeRecord) {
   315  	self.db.add(nrs, self.proximityBin)
   316  }
   317  
   318  // nodesByDistance is a list of nodes, ordered by distance to target.
   319  type nodesByDistance struct {
   320  	nodes  []Node
   321  	target Address
   322  }
   323  
   324  func sortedByDistanceTo(target Address, slice []Node) bool {
   325  	var last Address
   326  	for i, node := range slice {
   327  		if i > 0 {
   328  			if target.ProxCmp(node.Addr(), last) < 0 {
   329  				return false
   330  			}
   331  		}
   332  		last = node.Addr()
   333  	}
   334  	return true
   335  }
   336  
   337  // push(node, max) adds the given node to the list, keeping the total size
   338  // below max elements.
   339  func (h *nodesByDistance) push(node Node, max int) {
   340  	// returns the firt index ix such that func(i) returns true
   341  	ix := sort.Search(len(h.nodes), func(i int) bool {
   342  		return h.target.ProxCmp(h.nodes[i].Addr(), node.Addr()) >= 0
   343  	})
   344  
   345  	if len(h.nodes) < max {
   346  		h.nodes = append(h.nodes, node)
   347  	}
   348  	if ix < len(h.nodes) {
   349  		copy(h.nodes[ix+1:], h.nodes[ix:])
   350  		h.nodes[ix] = node
   351  	}
   352  }
   353  
   354  /*
   355  Taking the proximity order relative to a fix point x classifies the points in
   356  the space (n byte long byte sequences) into bins. Items in each are at
   357  most half as distant from x as items in the previous bin. Given a sample of
   358  uniformly distributed items (a hash function over arbitrary sequence) the
   359  proximity scale maps onto series of subsets with cardinalities on a negative
   360  exponential scale.
   361  
   362  It also has the property that any two item belonging to the same bin are at
   363  most half as distant from each other as they are from x.
   364  
   365  If we think of random sample of items in the bins as connections in a network of interconnected nodes than relative proximity can serve as the basis for local
   366  decisions for graph traversal where the task is to find a route between two
   367  points. Since in every hop, the finite distance halves, there is
   368  a guaranteed constant maximum limit on the number of hops needed to reach one
   369  node from the other.
   370  */
   371  
   372  func (self *Kademlia) proximityBin(other Address) (ret int) {
   373  	ret = proximity(self.addr, other)
   374  	if ret > self.MaxProx {
   375  		ret = self.MaxProx
   376  	}
   377  	return
   378  }
   379  
   380  // provides keyrange for chunk db iteration
   381  func (self *Kademlia) KeyRange(other Address) (start, stop Address) {
   382  	defer self.lock.RUnlock()
   383  	self.lock.RLock()
   384  	return KeyRange(self.addr, other, self.proxLimit)
   385  }
   386  
   387  // save persists kaddb on disk (written to file on path in json format.
   388  func (self *Kademlia) Save(path string, cb func(*NodeRecord, Node)) error {
   389  	return self.db.save(path, cb)
   390  }
   391  
   392  // Load(path) loads the node record database (kaddb) from file on path.
   393  func (self *Kademlia) Load(path string, cb func(*NodeRecord, Node) error) (err error) {
   394  	return self.db.load(path, cb)
   395  }
   396  
   397  // kademlia table + kaddb table displayed with ascii
   398  func (self *Kademlia) String() string {
   399  	defer self.lock.RUnlock()
   400  	self.lock.RLock()
   401  	defer self.db.lock.RUnlock()
   402  	self.db.lock.RLock()
   403  
   404  	var rows []string
   405  	rows = append(rows, "=========================================================================")
   406  	rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %v", time.Now().UTC().Format(time.UnixDate), self.addr.String()[:6]))
   407  	rows = append(rows, fmt.Sprintf("population: %d (%d), proxLimit: %d, proxSize: %d", self.count, len(self.db.index), self.proxLimit, self.proxSize))
   408  	rows = append(rows, fmt.Sprintf("MaxProx: %d, ProxBinSize: %d, BucketSize: %d", self.MaxProx, self.ProxBinSize, self.BucketSize))
   409  
   410  	for i, bucket := range self.buckets {
   411  
   412  		if i == self.proxLimit {
   413  			rows = append(rows, fmt.Sprintf("============ PROX LIMIT: %d ==========================================", i))
   414  		}
   415  		row := []string{fmt.Sprintf("%03d", i), fmt.Sprintf("%2d", len(bucket))}
   416  		var k int
   417  		c := self.db.cursors[i]
   418  		for ; k < len(bucket); k++ {
   419  			p := bucket[(c+k)%len(bucket)]
   420  			row = append(row, p.Addr().String()[:6])
   421  			if k == 4 {
   422  				break
   423  			}
   424  		}
   425  		for ; k < 4; k++ {
   426  			row = append(row, "      ")
   427  		}
   428  		row = append(row, fmt.Sprintf("| %2d %2d", len(self.db.Nodes[i]), self.db.cursors[i]))
   429  
   430  		for j, p := range self.db.Nodes[i] {
   431  			row = append(row, p.Addr.String()[:6])
   432  			if j == 3 {
   433  				break
   434  			}
   435  		}
   436  		rows = append(rows, strings.Join(row, " "))
   437  		if i == self.MaxProx {
   438  		}
   439  	}
   440  	rows = append(rows, "=========================================================================")
   441  	return strings.Join(rows, "\n")
   442  }
   443  
   444  //We have to build up the array of counters for each index
   445  func (self *Kademlia) initMetricsVariables() {
   446  	//create the arrays
   447  	bucketAddIndexCount = make([]metrics.Counter, self.MaxProx+1)
   448  	bucketRmIndexCount = make([]metrics.Counter, self.MaxProx+1)
   449  	//at each index create a metrics counter
   450  	for i := 0; i < (self.KadParams.MaxProx + 1); i++ {
   451  		bucketAddIndexCount[i] = metrics.NewRegisteredCounter(fmt.Sprintf("network.kademlia.bucket.add.%d.index", i), nil)
   452  		bucketRmIndexCount[i] = metrics.NewRegisteredCounter(fmt.Sprintf("network.kademlia.bucket.rm.%d.index", i), nil)
   453  	}
   454  }