github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/swarm/network/kademlia/kademlia.go (about)

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