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 }