github.com/digdeepmining/go-atheios@v1.5.13-0.20180902133602-d5687a2e6f43/swarm/network/kademlia/kaddb.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 "encoding/json" 21 "fmt" 22 "io/ioutil" 23 "os" 24 "sync" 25 "time" 26 27 "github.com/atheioschain/go-atheios/logger" 28 "github.com/atheioschain/go-atheios/logger/glog" 29 ) 30 31 type NodeData interface { 32 json.Marshaler 33 json.Unmarshaler 34 } 35 36 // allow inactive peers under 37 type NodeRecord struct { 38 Addr Address // address of node 39 Url string // Url, used to connect to node 40 After time.Time // next call after time 41 Seen time.Time // last connected at time 42 Meta *json.RawMessage // arbitrary metadata saved for a peer 43 44 node Node 45 } 46 47 func (self *NodeRecord) setSeen() { 48 t := time.Now() 49 self.Seen = t 50 self.After = t 51 } 52 53 func (self *NodeRecord) String() string { 54 return fmt.Sprintf("<%v>", self.Addr) 55 } 56 57 // persisted node record database () 58 type KadDb struct { 59 Address Address 60 Nodes [][]*NodeRecord 61 index map[Address]*NodeRecord 62 cursors []int 63 lock sync.RWMutex 64 purgeInterval time.Duration 65 initialRetryInterval time.Duration 66 connRetryExp int 67 } 68 69 func newKadDb(addr Address, params *KadParams) *KadDb { 70 return &KadDb{ 71 Address: addr, 72 Nodes: make([][]*NodeRecord, params.MaxProx+1), // overwritten by load 73 cursors: make([]int, params.MaxProx+1), 74 index: make(map[Address]*NodeRecord), 75 purgeInterval: params.PurgeInterval, 76 initialRetryInterval: params.InitialRetryInterval, 77 connRetryExp: params.ConnRetryExp, 78 } 79 } 80 81 func (self *KadDb) findOrCreate(index int, a Address, url string) *NodeRecord { 82 defer self.lock.Unlock() 83 self.lock.Lock() 84 85 record, found := self.index[a] 86 if !found { 87 record = &NodeRecord{ 88 Addr: a, 89 Url: url, 90 } 91 glog.V(logger.Info).Infof("add new record %v to kaddb", record) 92 // insert in kaddb 93 self.index[a] = record 94 self.Nodes[index] = append(self.Nodes[index], record) 95 } else { 96 glog.V(logger.Info).Infof("found record %v in kaddb", record) 97 } 98 // update last seen time 99 record.setSeen() 100 // update with url in case IP/port changes 101 record.Url = url 102 return record 103 } 104 105 // add adds node records to kaddb (persisted node record db) 106 func (self *KadDb) add(nrs []*NodeRecord, proximityBin func(Address) int) { 107 defer self.lock.Unlock() 108 self.lock.Lock() 109 var n int 110 var nodes []*NodeRecord 111 for _, node := range nrs { 112 _, found := self.index[node.Addr] 113 if !found && node.Addr != self.Address { 114 node.setSeen() 115 self.index[node.Addr] = node 116 index := proximityBin(node.Addr) 117 dbcursor := self.cursors[index] 118 nodes = self.Nodes[index] 119 // this is inefficient for allocation, need to just append then shift 120 newnodes := make([]*NodeRecord, len(nodes)+1) 121 copy(newnodes[:], nodes[:dbcursor]) 122 newnodes[dbcursor] = node 123 copy(newnodes[dbcursor+1:], nodes[dbcursor:]) 124 glog.V(logger.Detail).Infof("new nodes: %v (keys: %v)\nnodes: %v", newnodes, nodes) 125 self.Nodes[index] = newnodes 126 n++ 127 } 128 } 129 if n > 0 { 130 glog.V(logger.Debug).Infof("%d/%d node records (new/known)", n, len(nrs)) 131 } 132 } 133 134 /* 135 next return one node record with the highest priority for desired 136 connection. 137 This is used to pick candidates for live nodes that are most wanted for 138 a higly connected low centrality network structure for Swarm which best suits 139 for a Kademlia-style routing. 140 141 * Starting as naive node with empty db, this implements Kademlia bootstrapping 142 * As a mature node, it fills short lines. All on demand. 143 144 The candidate is chosen using the following strategy: 145 We check for missing online nodes in the buckets for 1 upto Max BucketSize rounds. 146 On each round we proceed from the low to high proximity order buckets. 147 If the number of active nodes (=connected peers) is < rounds, then start looking 148 for a known candidate. To determine if there is a candidate to recommend the 149 kaddb node record database row corresponding to the bucket is checked. 150 151 If the row cursor is on position i, the ith element in the row is chosen. 152 If the record is scheduled not to be retried before NOW, the next element is taken. 153 If the record is scheduled to be retried, it is set as checked, scheduled for 154 checking and is returned. The time of the next check is in X (duration) such that 155 X = ConnRetryExp * delta where delta is the time past since the last check and 156 ConnRetryExp is constant obsoletion factor. (Note that when node records are added 157 from peer messages, they are marked as checked and placed at the cursor, ie. 158 given priority over older entries). Entries which were checked more than 159 purgeInterval ago are deleted from the kaddb row. If no candidate is found after 160 a full round of checking the next bucket up is considered. If no candidate is 161 found when we reach the maximum-proximity bucket, the next round starts. 162 163 node record a is more favoured to b a > b iff a is a passive node (record of 164 offline past peer) 165 |proxBin(a)| < |proxBin(b)| 166 || (proxBin(a) < proxBin(b) && |proxBin(a)| == |proxBin(b)|) 167 || (proxBin(a) == proxBin(b) && lastChecked(a) < lastChecked(b)) 168 169 170 The second argument returned names the first missing slot found 171 */ 172 func (self *KadDb) findBest(maxBinSize int, binSize func(int) int) (node *NodeRecord, need bool, proxLimit int) { 173 // return nil, proxLimit indicates that all buckets are filled 174 defer self.lock.Unlock() 175 self.lock.Lock() 176 177 var interval time.Duration 178 var found bool 179 var purge []bool 180 var delta time.Duration 181 var cursor int 182 var count int 183 var after time.Time 184 185 // iterate over columns maximum bucketsize times 186 for rounds := 1; rounds <= maxBinSize; rounds++ { 187 ROUND: 188 // iterate over rows from PO 0 upto MaxProx 189 for po, dbrow := range self.Nodes { 190 // if row has rounds connected peers, then take the next 191 if binSize(po) >= rounds { 192 continue ROUND 193 } 194 if !need { 195 // set proxlimit to the PO where the first missing slot is found 196 proxLimit = po 197 need = true 198 } 199 purge = make([]bool, len(dbrow)) 200 201 // there is a missing slot - finding a node to connect to 202 // select a node record from the relavant kaddb row (of identical prox order) 203 ROW: 204 for cursor = self.cursors[po]; !found && count < len(dbrow); cursor = (cursor + 1) % len(dbrow) { 205 count++ 206 node = dbrow[cursor] 207 208 // skip already connected nodes 209 if node.node != nil { 210 glog.V(logger.Debug).Infof("kaddb record %v (PO%03d:%d/%d) already connected", node.Addr, po, cursor, len(dbrow)) 211 continue ROW 212 } 213 214 // if node is scheduled to connect 215 if time.Time(node.After).After(time.Now()) { 216 glog.V(logger.Debug).Infof("kaddb record %v (PO%03d:%d) skipped. seen at %v (%v ago), scheduled at %v", node.Addr, po, cursor, node.Seen, delta, node.After) 217 continue ROW 218 } 219 220 delta = time.Since(time.Time(node.Seen)) 221 if delta < self.initialRetryInterval { 222 delta = self.initialRetryInterval 223 } 224 if delta > self.purgeInterval { 225 // remove node 226 purge[cursor] = true 227 glog.V(logger.Debug).Infof("kaddb record %v (PO%03d:%d) unreachable since %v. Removed", node.Addr, po, cursor, node.Seen) 228 continue ROW 229 } 230 231 glog.V(logger.Debug).Infof("kaddb record %v (PO%03d:%d) ready to be tried. seen at %v (%v ago), scheduled at %v", node.Addr, po, cursor, node.Seen, delta, node.After) 232 233 // scheduling next check 234 interval = time.Duration(delta * time.Duration(self.connRetryExp)) 235 after = time.Now().Add(interval) 236 237 glog.V(logger.Debug).Infof("kaddb record %v (PO%03d:%d) selected as candidate connection %v. seen at %v (%v ago), selectable since %v, retry after %v (in %v)", node.Addr, po, cursor, rounds, node.Seen, delta, node.After, after, interval) 238 node.After = after 239 found = true 240 } // ROW 241 self.cursors[po] = cursor 242 self.delete(po, purge) 243 if found { 244 return node, need, proxLimit 245 } 246 } // ROUND 247 } // ROUNDS 248 249 return nil, need, proxLimit 250 } 251 252 // deletes the noderecords of a kaddb row corresponding to the indexes 253 // caller must hold the dblock 254 // the call is unsafe, no index checks 255 func (self *KadDb) delete(row int, purge []bool) { 256 var nodes []*NodeRecord 257 dbrow := self.Nodes[row] 258 for i, del := range purge { 259 if i == self.cursors[row] { 260 //reset cursor 261 self.cursors[row] = len(nodes) 262 } 263 // delete the entry to be purged 264 if del { 265 delete(self.index, dbrow[i].Addr) 266 continue 267 } 268 // otherwise append to new list 269 nodes = append(nodes, dbrow[i]) 270 } 271 self.Nodes[row] = nodes 272 } 273 274 // save persists kaddb on disk (written to file on path in json format. 275 func (self *KadDb) save(path string, cb func(*NodeRecord, Node)) error { 276 defer self.lock.Unlock() 277 self.lock.Lock() 278 279 var n int 280 281 for _, b := range self.Nodes { 282 for _, node := range b { 283 n++ 284 node.After = time.Now() 285 node.Seen = time.Now() 286 if cb != nil { 287 cb(node, node.node) 288 } 289 } 290 } 291 292 data, err := json.MarshalIndent(self, "", " ") 293 if err != nil { 294 return err 295 } 296 err = ioutil.WriteFile(path, data, os.ModePerm) 297 if err != nil { 298 glog.V(logger.Warn).Infof("unable to save kaddb with %v nodes to %v: err", n, path, err) 299 } else { 300 glog.V(logger.Info).Infof("saved kaddb with %v nodes to %v", n, path) 301 } 302 return err 303 } 304 305 // Load(path) loads the node record database (kaddb) from file on path. 306 func (self *KadDb) load(path string, cb func(*NodeRecord, Node) error) (err error) { 307 defer self.lock.Unlock() 308 self.lock.Lock() 309 310 var data []byte 311 data, err = ioutil.ReadFile(path) 312 if err != nil { 313 return 314 } 315 316 err = json.Unmarshal(data, self) 317 if err != nil { 318 return 319 } 320 var n int 321 var purge []bool 322 for po, b := range self.Nodes { 323 purge = make([]bool, len(b)) 324 ROW: 325 for i, node := range b { 326 if cb != nil { 327 err = cb(node, node.node) 328 if err != nil { 329 purge[i] = true 330 continue ROW 331 } 332 } 333 n++ 334 if (node.After == time.Time{}) { 335 node.After = time.Now() 336 } 337 self.index[node.Addr] = node 338 } 339 self.delete(po, purge) 340 } 341 glog.V(logger.Info).Infof("loaded kaddb with %v nodes from %v", n, path) 342 343 return 344 } 345 346 // accessor for KAD offline db count 347 func (self *KadDb) count() int { 348 defer self.lock.Unlock() 349 self.lock.Lock() 350 return len(self.index) 351 }