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 }