github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/network/p2p/discv5/database.go (about) 1 package discv5 2 3 import ( 4 "bytes" 5 "crypto/rand" 6 "encoding/binary" 7 "fmt" 8 "os" 9 "sync" 10 "time" 11 12 "github.com/neatlab/neatio/chain/log" 13 "github.com/neatlab/neatio/utilities/crypto" 14 "github.com/neatlab/neatio/utilities/rlp" 15 "github.com/syndtr/goleveldb/leveldb" 16 "github.com/syndtr/goleveldb/leveldb/errors" 17 "github.com/syndtr/goleveldb/leveldb/iterator" 18 "github.com/syndtr/goleveldb/leveldb/opt" 19 "github.com/syndtr/goleveldb/leveldb/storage" 20 "github.com/syndtr/goleveldb/leveldb/util" 21 ) 22 23 var ( 24 nodeDBNilNodeID = NodeID{} 25 nodeDBNodeExpiration = 24 * time.Hour 26 nodeDBCleanupCycle = time.Hour 27 ) 28 29 type nodeDB struct { 30 lvl *leveldb.DB 31 self NodeID 32 runner sync.Once 33 quit chan struct{} 34 } 35 36 var ( 37 nodeDBVersionKey = []byte("version") 38 nodeDBItemPrefix = []byte("n:") 39 40 nodeDBDiscoverRoot = ":discover" 41 nodeDBDiscoverPing = nodeDBDiscoverRoot + ":lastping" 42 nodeDBDiscoverPong = nodeDBDiscoverRoot + ":lastpong" 43 nodeDBDiscoverFindFails = nodeDBDiscoverRoot + ":findfail" 44 nodeDBDiscoverLocalEndpoint = nodeDBDiscoverRoot + ":localendpoint" 45 nodeDBTopicRegTickets = ":tickets" 46 ) 47 48 func newNodeDB(path string, version int, self NodeID) (*nodeDB, error) { 49 if path == "" { 50 return newMemoryNodeDB(self) 51 } 52 return newPersistentNodeDB(path, version, self) 53 } 54 55 func newMemoryNodeDB(self NodeID) (*nodeDB, error) { 56 db, err := leveldb.Open(storage.NewMemStorage(), nil) 57 if err != nil { 58 return nil, err 59 } 60 return &nodeDB{ 61 lvl: db, 62 self: self, 63 quit: make(chan struct{}), 64 }, nil 65 } 66 67 func newPersistentNodeDB(path string, version int, self NodeID) (*nodeDB, error) { 68 opts := &opt.Options{OpenFilesCacheCapacity: 5} 69 db, err := leveldb.OpenFile(path, opts) 70 if _, iscorrupted := err.(*errors.ErrCorrupted); iscorrupted { 71 db, err = leveldb.RecoverFile(path, nil) 72 } 73 if err != nil { 74 return nil, err 75 } 76 77 currentVer := make([]byte, binary.MaxVarintLen64) 78 currentVer = currentVer[:binary.PutVarint(currentVer, int64(version))] 79 80 blob, err := db.Get(nodeDBVersionKey, nil) 81 switch err { 82 case leveldb.ErrNotFound: 83 84 if err := db.Put(nodeDBVersionKey, currentVer, nil); err != nil { 85 db.Close() 86 return nil, err 87 } 88 89 case nil: 90 91 if !bytes.Equal(blob, currentVer) { 92 db.Close() 93 if err = os.RemoveAll(path); err != nil { 94 return nil, err 95 } 96 return newPersistentNodeDB(path, version, self) 97 } 98 } 99 return &nodeDB{ 100 lvl: db, 101 self: self, 102 quit: make(chan struct{}), 103 }, nil 104 } 105 106 func makeKey(id NodeID, field string) []byte { 107 if bytes.Equal(id[:], nodeDBNilNodeID[:]) { 108 return []byte(field) 109 } 110 return append(nodeDBItemPrefix, append(id[:], field...)...) 111 } 112 113 func splitKey(key []byte) (id NodeID, field string) { 114 115 if !bytes.HasPrefix(key, nodeDBItemPrefix) { 116 return NodeID{}, string(key) 117 } 118 119 item := key[len(nodeDBItemPrefix):] 120 copy(id[:], item[:len(id)]) 121 field = string(item[len(id):]) 122 123 return id, field 124 } 125 126 func (db *nodeDB) fetchInt64(key []byte) int64 { 127 blob, err := db.lvl.Get(key, nil) 128 if err != nil { 129 return 0 130 } 131 val, read := binary.Varint(blob) 132 if read <= 0 { 133 return 0 134 } 135 return val 136 } 137 138 func (db *nodeDB) storeInt64(key []byte, n int64) error { 139 blob := make([]byte, binary.MaxVarintLen64) 140 blob = blob[:binary.PutVarint(blob, n)] 141 return db.lvl.Put(key, blob, nil) 142 } 143 144 func (db *nodeDB) storeRLP(key []byte, val interface{}) error { 145 blob, err := rlp.EncodeToBytes(val) 146 if err != nil { 147 return err 148 } 149 return db.lvl.Put(key, blob, nil) 150 } 151 152 func (db *nodeDB) fetchRLP(key []byte, val interface{}) error { 153 blob, err := db.lvl.Get(key, nil) 154 if err != nil { 155 return err 156 } 157 err = rlp.DecodeBytes(blob, val) 158 if err != nil { 159 log.Warn(fmt.Sprintf("key %x (%T) %v", key, val, err)) 160 } 161 return err 162 } 163 164 func (db *nodeDB) node(id NodeID) *Node { 165 var node Node 166 if err := db.fetchRLP(makeKey(id, nodeDBDiscoverRoot), &node); err != nil { 167 return nil 168 } 169 node.sha = crypto.Keccak256Hash(node.ID[:]) 170 return &node 171 } 172 173 func (db *nodeDB) updateNode(node *Node) error { 174 return db.storeRLP(makeKey(node.ID, nodeDBDiscoverRoot), node) 175 } 176 177 func (db *nodeDB) deleteNode(id NodeID) error { 178 deleter := db.lvl.NewIterator(util.BytesPrefix(makeKey(id, "")), nil) 179 for deleter.Next() { 180 if err := db.lvl.Delete(deleter.Key(), nil); err != nil { 181 return err 182 } 183 } 184 return nil 185 } 186 187 func (db *nodeDB) ensureExpirer() { 188 db.runner.Do(func() { go db.expirer() }) 189 } 190 191 func (db *nodeDB) expirer() { 192 tick := time.NewTicker(nodeDBCleanupCycle) 193 defer tick.Stop() 194 for { 195 select { 196 case <-tick.C: 197 if err := db.expireNodes(); err != nil { 198 log.Error(fmt.Sprintf("Failed to expire nodedb items: %v", err)) 199 } 200 case <-db.quit: 201 return 202 } 203 } 204 } 205 206 func (db *nodeDB) expireNodes() error { 207 threshold := time.Now().Add(-nodeDBNodeExpiration) 208 209 it := db.lvl.NewIterator(nil, nil) 210 defer it.Release() 211 212 for it.Next() { 213 214 id, field := splitKey(it.Key()) 215 if field != nodeDBDiscoverRoot { 216 continue 217 } 218 219 if !bytes.Equal(id[:], db.self[:]) { 220 if seen := db.lastPong(id); seen.After(threshold) { 221 continue 222 } 223 } 224 225 db.deleteNode(id) 226 } 227 return nil 228 } 229 230 func (db *nodeDB) lastPing(id NodeID) time.Time { 231 return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPing)), 0) 232 } 233 234 func (db *nodeDB) updateLastPing(id NodeID, instance time.Time) error { 235 return db.storeInt64(makeKey(id, nodeDBDiscoverPing), instance.Unix()) 236 } 237 238 func (db *nodeDB) lastPong(id NodeID) time.Time { 239 return time.Unix(db.fetchInt64(makeKey(id, nodeDBDiscoverPong)), 0) 240 } 241 242 func (db *nodeDB) updateLastPong(id NodeID, instance time.Time) error { 243 return db.storeInt64(makeKey(id, nodeDBDiscoverPong), instance.Unix()) 244 } 245 246 func (db *nodeDB) findFails(id NodeID) int { 247 return int(db.fetchInt64(makeKey(id, nodeDBDiscoverFindFails))) 248 } 249 250 func (db *nodeDB) updateFindFails(id NodeID, fails int) error { 251 return db.storeInt64(makeKey(id, nodeDBDiscoverFindFails), int64(fails)) 252 } 253 254 func (db *nodeDB) localEndpoint(id NodeID) *rpcEndpoint { 255 var ep rpcEndpoint 256 if err := db.fetchRLP(makeKey(id, nodeDBDiscoverLocalEndpoint), &ep); err != nil { 257 return nil 258 } 259 return &ep 260 } 261 262 func (db *nodeDB) updateLocalEndpoint(id NodeID, ep rpcEndpoint) error { 263 return db.storeRLP(makeKey(id, nodeDBDiscoverLocalEndpoint), &ep) 264 } 265 266 func (db *nodeDB) querySeeds(n int, maxAge time.Duration) []*Node { 267 var ( 268 now = time.Now() 269 nodes = make([]*Node, 0, n) 270 it = db.lvl.NewIterator(nil, nil) 271 id NodeID 272 ) 273 defer it.Release() 274 275 seek: 276 for seeks := 0; len(nodes) < n && seeks < n*5; seeks++ { 277 278 ctr := id[0] 279 rand.Read(id[:]) 280 id[0] = ctr + id[0]%16 281 it.Seek(makeKey(id, nodeDBDiscoverRoot)) 282 283 n := nextNode(it) 284 if n == nil { 285 id[0] = 0 286 continue seek 287 } 288 if n.ID == db.self { 289 continue seek 290 } 291 if now.Sub(db.lastPong(n.ID)) > maxAge { 292 continue seek 293 } 294 for i := range nodes { 295 if nodes[i].ID == n.ID { 296 continue seek 297 } 298 } 299 nodes = append(nodes, n) 300 } 301 return nodes 302 } 303 304 func (db *nodeDB) fetchTopicRegTickets(id NodeID) (issued, used uint32) { 305 key := makeKey(id, nodeDBTopicRegTickets) 306 blob, _ := db.lvl.Get(key, nil) 307 if len(blob) != 8 { 308 return 0, 0 309 } 310 issued = binary.BigEndian.Uint32(blob[0:4]) 311 used = binary.BigEndian.Uint32(blob[4:8]) 312 return 313 } 314 315 func (db *nodeDB) updateTopicRegTickets(id NodeID, issued, used uint32) error { 316 key := makeKey(id, nodeDBTopicRegTickets) 317 blob := make([]byte, 8) 318 binary.BigEndian.PutUint32(blob[0:4], issued) 319 binary.BigEndian.PutUint32(blob[4:8], used) 320 return db.lvl.Put(key, blob, nil) 321 } 322 323 func nextNode(it iterator.Iterator) *Node { 324 for end := false; !end; end = !it.Next() { 325 id, field := splitKey(it.Key()) 326 if field != nodeDBDiscoverRoot { 327 continue 328 } 329 var n Node 330 if err := rlp.DecodeBytes(it.Value(), &n); err != nil { 331 log.Warn(fmt.Sprintf("invalid node %x: %v", id, err)) 332 continue 333 } 334 return &n 335 } 336 return nil 337 } 338 339 func (db *nodeDB) close() { 340 close(db.quit) 341 db.lvl.Close() 342 }