github.com/annchain/OG@v0.0.9/p2p/onode/nodedb.go (about) 1 // Copyright 2015 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 onode 18 19 import ( 20 "bytes" 21 "crypto/rand" 22 "encoding/binary" 23 "fmt" 24 "github.com/annchain/OG/arefactor/common/goroutine" 25 "github.com/annchain/OG/p2p/enr" 26 "github.com/syndtr/goleveldb/leveldb" 27 "github.com/syndtr/goleveldb/leveldb/errors" 28 "github.com/syndtr/goleveldb/leveldb/iterator" 29 "github.com/syndtr/goleveldb/leveldb/opt" 30 "github.com/syndtr/goleveldb/leveldb/storage" 31 "github.com/syndtr/goleveldb/leveldb/util" 32 "os" 33 "sync" 34 "time" 35 ) 36 37 // Keys in the node database. 38 const ( 39 dbVersionKey = "version" // Version of the database to flush if changes 40 dbItemPrefix = "n:" // Identifier to prefix node entries with 41 42 dbDiscoverRoot = ":discover" 43 dbDiscoverSeq = dbDiscoverRoot + ":seq" 44 dbDiscoverPing = dbDiscoverRoot + ":lastping" 45 dbDiscoverPong = dbDiscoverRoot + ":lastpong" 46 dbDiscoverFindFails = dbDiscoverRoot + ":findfail" 47 dbLocalRoot = ":local" 48 dbLocalSeq = dbLocalRoot + ":seq" 49 ) 50 51 var ( 52 dbNodeExpiration = 24 * time.Hour // Time after which an unseen node should be dropped. 53 dbCleanupCycle = time.Hour // Time period for running the expiration task. 54 dbVersion = 7 55 ) 56 57 // DB is the node database, storing previously seen nodes and any collected metadata about 58 // them for QoS purposes. 59 type DB struct { 60 lvl *leveldb.DB // Interface to the database itself 61 runner sync.Once // Ensures we can start at most one expirer 62 quit chan struct{} // Channel to signal the expiring thread to stop 63 } 64 65 // OpenDB opens a node database for storing and retrieving infos about known peers in the 66 // network. If no path is given an in-memory, temporary database is constructed. 67 func OpenDB(path string) (*DB, error) { 68 if path == "" { 69 return newMemoryDB() 70 } 71 return newPersistentDB(path) 72 } 73 74 // newMemoryNodeDB creates a new in-memory node database without a persistent backend. 75 func newMemoryDB() (*DB, error) { 76 db, err := leveldb.Open(storage.NewMemStorage(), nil) 77 if err != nil { 78 return nil, err 79 } 80 return &DB{lvl: db, quit: make(chan struct{})}, nil 81 } 82 83 // newPersistentNodeDB creates/opens a leveldb backed persistent node database, 84 // also flushing its contents in case of a version mismatch. 85 func newPersistentDB(path string) (*DB, error) { 86 opts := &opt.Options{OpenFilesCacheCapacity: 5} 87 db, err := leveldb.OpenFile(path, opts) 88 if _, iscorrupted := err.(*errors.ErrCorrupted); iscorrupted { 89 db, err = leveldb.RecoverFile(path, nil) 90 } 91 if err != nil { 92 return nil, err 93 } 94 // The nodes contained in the cache correspond to a certain protocol version. 95 // Flush all nodes if the version doesn't match. 96 currentVer := make([]byte, binary.MaxVarintLen64) 97 currentVer = currentVer[:binary.PutVarint(currentVer, int64(dbVersion))] 98 99 blob, err := db.Get([]byte(dbVersionKey), nil) 100 switch err { 101 case leveldb.ErrNotFound: 102 // Version not found (i.e. empty cache), insert it 103 if err := db.Put([]byte(dbVersionKey), currentVer, nil); err != nil { 104 db.Close() 105 return nil, err 106 } 107 108 case nil: 109 // Version present, flush if different 110 if !bytes.Equal(blob, currentVer) { 111 db.Close() 112 if err = os.RemoveAll(path); err != nil { 113 return nil, err 114 } 115 return newPersistentDB(path) 116 } 117 } 118 return &DB{lvl: db, quit: make(chan struct{})}, nil 119 } 120 121 // makeKey generates the leveldb key-blob from a node id and its particular 122 // field of interest. 123 func makeKey(id ID, field string) []byte { 124 if (id == ID{}) { 125 return []byte(field) 126 } 127 return append([]byte(dbItemPrefix), append(id[:], field...)...) 128 } 129 130 // splitKey tries to split a database key into a node id and a field part. 131 func splitKey(key []byte) (id ID, field string) { 132 // If the key is not of a node, return it plainly 133 if !bytes.HasPrefix(key, []byte(dbItemPrefix)) { 134 return ID{}, string(key) 135 } 136 // Otherwise split the id and field 137 item := key[len(dbItemPrefix):] 138 copy(id[:], item[:len(id)]) 139 field = string(item[len(id):]) 140 141 return id, field 142 } 143 144 // fetchInt64 retrieves an integer associated with a particular key. 145 func (db *DB) fetchInt64(key []byte) int64 { 146 blob, err := db.lvl.Get(key, nil) 147 if err != nil { 148 return 0 149 } 150 val, read := binary.Varint(blob) 151 if read <= 0 { 152 return 0 153 } 154 return val 155 } 156 157 // storeInt64 stores an integer in the given key. 158 func (db *DB) storeInt64(key []byte, n int64) error { 159 blob := make([]byte, binary.MaxVarintLen64) 160 blob = blob[:binary.PutVarint(blob, n)] 161 return db.lvl.Put(key, blob, nil) 162 } 163 164 // fetchUint64 retrieves an integer associated with a particular key. 165 func (db *DB) fetchUint64(key []byte) uint64 { 166 blob, err := db.lvl.Get(key, nil) 167 if err != nil { 168 return 0 169 } 170 val, _ := binary.Uvarint(blob) 171 return val 172 } 173 174 // storeUint64 stores an integer in the given key. 175 func (db *DB) storeUint64(key []byte, n uint64) error { 176 blob := make([]byte, binary.MaxVarintLen64) 177 blob = blob[:binary.PutUvarint(blob, n)] 178 return db.lvl.Put(key, blob, nil) 179 } 180 181 // Node retrieves a node with a given id from the database. 182 func (db *DB) Node(id ID) *Node { 183 blob, err := db.lvl.Get(makeKey(id, dbDiscoverRoot), nil) 184 if err != nil { 185 return nil 186 } 187 return mustDecodeNode(id[:], blob) 188 } 189 190 func mustDecodeNode(id, data []byte) *Node { 191 node := new(Node) 192 var r enr.Record 193 if _, err := r.Decode(data); err != nil { 194 panic(fmt.Errorf("p2p/onode: can't decode node %x in DB: %v", id, err)) 195 } 196 node.R = r 197 // Restore node id cache. 198 copy(node.Id[:], id) 199 return node 200 } 201 202 // UpdateNode inserts - potentially overwriting - a node into the peer database. 203 func (db *DB) UpdateNode(node *Node) error { 204 if node.Seq() < db.NodeSeq(node.ID()) { 205 return nil 206 } 207 blob, err := node.R.Encode(nil) 208 if err != nil { 209 return err 210 } 211 if err := db.lvl.Put(makeKey(node.ID(), dbDiscoverRoot), blob, nil); err != nil { 212 return err 213 } 214 return db.storeUint64(makeKey(node.ID(), dbDiscoverSeq), node.Seq()) 215 } 216 217 // NodeSeq returns the stored record sequence number of the given node. 218 func (db *DB) NodeSeq(id ID) uint64 { 219 return db.fetchUint64(makeKey(id, dbDiscoverSeq)) 220 } 221 222 // Resolve returns the stored record of the node if it has a larger sequence 223 // number than n. 224 func (db *DB) Resolve(n *Node) *Node { 225 if n.Seq() > db.NodeSeq(n.ID()) { 226 return n 227 } 228 return db.Node(n.ID()) 229 } 230 231 // DeleteNode deletes all information/keys associated with a node. 232 func (db *DB) DeleteNode(id ID) error { 233 deleter := db.lvl.NewIterator(util.BytesPrefix(makeKey(id, "")), nil) 234 for deleter.Next() { 235 if err := db.lvl.Delete(deleter.Key(), nil); err != nil { 236 return err 237 } 238 } 239 return nil 240 } 241 242 // ensureExpirer is a small helper method ensuring that the data expiration 243 // mechanism is running. If the expiration goroutine is already running, this 244 // method simply returns. 245 // 246 // The goal is to start the data evacuation only after the network successfully 247 // bootstrapped itself (to prevent dumping potentially useful seed nodes). Since 248 // it would require significant overhead to exactly trace the first successful 249 // convergence, it's simpler to "ensure" the correct state when an appropriate 250 // condition occurs (i.e. a successful bonding), and discard further events. 251 func (db *DB) ensureExpirer() { 252 db.runner.Do(func() { goroutine.New(db.expirer) }) 253 } 254 255 // expirer should be started in a go routine, and is responsible for looping ad 256 // infinitum and dropping stale data from the database. 257 func (db *DB) expirer() { 258 tick := time.NewTicker(dbCleanupCycle) 259 defer tick.Stop() 260 for { 261 select { 262 case <-tick.C: 263 if err := db.expireNodes(); err != nil { 264 log.Error("Failed to expire nodedb items", "err", err) 265 } 266 case <-db.quit: 267 return 268 } 269 } 270 } 271 272 // expireNodes iterates over the database and deletes all nodes that have not 273 // been seen (i.e. received a pong from) for some allotted time. 274 func (db *DB) expireNodes() error { 275 threshold := time.Now().Add(-dbNodeExpiration) 276 277 // Find discovered nodes that are older than the allowance 278 it := db.lvl.NewIterator(nil, nil) 279 defer it.Release() 280 281 for it.Next() { 282 // Skip the item if not a discovery node 283 id, field := splitKey(it.Key()) 284 if field != dbDiscoverRoot { 285 continue 286 } 287 // Skip the node if not expired yet (and not self) 288 if seen := db.LastPongReceived(id); seen.After(threshold) { 289 continue 290 } 291 // Otherwise delete all associated information 292 db.DeleteNode(id) 293 } 294 return nil 295 } 296 297 // LastPingReceived retrieves the time of the last ping packet received from 298 // a remote node. 299 func (db *DB) LastPingReceived(id ID) time.Time { 300 return time.Unix(db.fetchInt64(makeKey(id, dbDiscoverPing)), 0) 301 } 302 303 // UpdateLastPingReceived updates the last time we tried contacting a remote node. 304 func (db *DB) UpdateLastPingReceived(id ID, instance time.Time) error { 305 return db.storeInt64(makeKey(id, dbDiscoverPing), instance.Unix()) 306 } 307 308 // LastPongReceived retrieves the time of the last successful pong from remote node. 309 func (db *DB) LastPongReceived(id ID) time.Time { 310 // Launch expirer 311 db.ensureExpirer() 312 return time.Unix(db.fetchInt64(makeKey(id, dbDiscoverPong)), 0) 313 } 314 315 // UpdateLastPongReceived updates the last pong time of a node. 316 func (db *DB) UpdateLastPongReceived(id ID, instance time.Time) error { 317 return db.storeInt64(makeKey(id, dbDiscoverPong), instance.Unix()) 318 } 319 320 // FindFails retrieves the number of findnode failures since bonding. 321 func (db *DB) FindFails(id ID) int { 322 return int(db.fetchInt64(makeKey(id, dbDiscoverFindFails))) 323 } 324 325 // UpdateFindFails updates the number of findnode failures since bonding. 326 func (db *DB) UpdateFindFails(id ID, fails int) error { 327 return db.storeInt64(makeKey(id, dbDiscoverFindFails), int64(fails)) 328 } 329 330 // LocalSeq retrieves the local record sequence counter. 331 func (db *DB) localSeq(id ID) uint64 { 332 return db.fetchUint64(makeKey(id, dbLocalSeq)) 333 } 334 335 // storeLocalSeq stores the local record sequence counter. 336 func (db *DB) storeLocalSeq(id ID, n uint64) { 337 db.storeUint64(makeKey(id, dbLocalSeq), n) 338 } 339 340 // QuerySeeds retrieves random nodes to be used as potential seed nodes 341 // for bootstrapping. 342 func (db *DB) QuerySeeds(n int, maxAge time.Duration) []*Node { 343 var ( 344 now = time.Now() 345 nodes = make([]*Node, 0, n) 346 it = db.lvl.NewIterator(nil, nil) 347 id ID 348 ) 349 defer it.Release() 350 351 seek: 352 for seeks := 0; len(nodes) < n && seeks < n*5; seeks++ { 353 // Seek to a random entry. The first byte is incremented by a 354 // random amount each time in order to increase the likelihood 355 // of hitting all existing nodes in very small databases. 356 ctr := id[0] 357 rand.Read(id[:]) 358 id[0] = ctr + id[0]%16 359 it.Seek(makeKey(id, dbDiscoverRoot)) 360 361 n := nextNode(it) 362 if n == nil { 363 id[0] = 0 364 continue seek // iterator exhausted 365 } 366 if now.Sub(db.LastPongReceived(n.ID())) > maxAge { 367 continue seek 368 } 369 for i := range nodes { 370 if nodes[i].ID() == n.ID() { 371 continue seek // duplicate 372 } 373 } 374 nodes = append(nodes, n) 375 } 376 return nodes 377 } 378 379 // reads the next node record from the iterator, skipping over other 380 // database entries. 381 func nextNode(it iterator.Iterator) *Node { 382 for end := false; !end; end = !it.Next() { 383 id, field := splitKey(it.Key()) 384 if field != dbDiscoverRoot { 385 continue 386 } 387 return mustDecodeNode(id[:], it.Value()) 388 } 389 return nil 390 } 391 392 // close flushes and closes the database files. 393 func (db *DB) Close() { 394 close(db.quit) 395 db.lvl.Close() 396 }