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