github.com/amazechain/amc@v0.1.3/internal/p2p/enode/nodedb.go (about) 1 // Copyright 2018 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 enode 18 19 import ( 20 "bytes" 21 "context" 22 "crypto/rand" 23 "encoding/binary" 24 "errors" 25 "fmt" 26 "github.com/amazechain/amc/internal/avm/rlp" 27 "github.com/c2h5oh/datasize" 28 libcommon "github.com/ledgerwatch/erigon-lib/common" 29 "github.com/ledgerwatch/erigon-lib/kv" 30 "github.com/ledgerwatch/erigon-lib/kv/mdbx" 31 "github.com/ledgerwatch/log/v3" 32 "net" 33 "os" 34 "sync" 35 "time" 36 37 mdbx1 "github.com/erigontech/mdbx-go/mdbx" 38 /* 39 "github.com/syndtr/goleveldb/leveldb" 40 "github.com/syndtr/goleveldb/leveldb/errors" 41 "github.com/syndtr/goleveldb/leveldb/iterator" 42 "github.com/syndtr/goleveldb/leveldb/opt" 43 "github.com/syndtr/goleveldb/leveldb/storage" 44 "github.com/syndtr/goleveldb/leveldb/util" 45 46 */) 47 48 // Keys in the node database. 49 const ( 50 dbVersionKey = "version" // Version of the database to flush if changes 51 dbNodePrefix = "n:" // Identifier to prefix node entries with 52 dbLocalPrefix = "local:" 53 dbDiscoverRoot = "v4" 54 dbDiscv5Root = "v5" 55 56 // These fields are stored per ID and IP, the full key is "n:<ID>:v4:<IP>:findfail". 57 // Use nodeItemKey to create those keys. 58 dbNodeFindFails = "findfail" 59 dbNodePing = "lastping" 60 dbNodePong = "lastpong" 61 dbNodeSeq = "seq" 62 63 // Local information is keyed by ID only, the full key is "local:<ID>:seq". 64 // Use localItemKey to create those keys. 65 dbLocalSeq = "seq" 66 ) 67 68 const ( 69 dbNodeExpiration = 24 * time.Hour // Time after which an unseen node should be dropped. 70 dbCleanupCycle = time.Hour // Time period for running the expiration task. 71 dbVersion = 9 72 ) 73 74 var ( 75 errInvalidIP = errors.New("invalid IP") 76 ) 77 78 var zeroIP = make(net.IP, 16) 79 80 // DB is the node database, storing previously seen nodes and any collected metadata about 81 // them for QoS purposes. 82 type DB struct { 83 kv kv.RwDB // Interface to the database itself 84 runner sync.Once // Ensures we can start at most one expirer 85 quit chan struct{} // Channel to signal the expiring thread to stop 86 } 87 88 // OpenDB opens a node database for storing and retrieving infos about known peers in the 89 // network. If no path is given an in-memory, temporary database is constructed. 90 func OpenDB(path string, tmpDir string) (*DB, error) { 91 logger := log.New() 92 if path == "" { 93 return newMemoryDB(logger, tmpDir) 94 } 95 return newPersistentDB(logger, path) 96 } 97 98 func bucketsConfig(_ kv.TableCfg) kv.TableCfg { 99 return kv.TableCfg{ 100 kv.Inodes: {}, 101 kv.NodeRecords: {}, 102 } 103 } 104 105 // newMemoryNodeDB creates a new in-memory node database without a persistent backend. 106 func newMemoryDB(logger log.Logger, tmpDir string) (*DB, error) { 107 db := &DB{quit: make(chan struct{})} 108 var err error 109 db.kv, err = mdbx.NewMDBX(logger).InMem(tmpDir).Label(kv.SentryDB).WithTableCfg(bucketsConfig).MapSize(1 * datasize.GB).Open() 110 if err != nil { 111 return nil, err 112 } 113 return db, nil 114 } 115 116 // newPersistentNodeDB creates/opens a persistent node database, 117 // also flushing its contents in case of a version mismatch. 118 func newPersistentDB(logger log.Logger, path string) (*DB, error) { 119 var db kv.RwDB 120 var err error 121 db, err = mdbx.NewMDBX(logger). 122 Path(path). 123 Label(kv.SentryDB). 124 WithTableCfg(bucketsConfig). 125 MapSize(1024 * datasize.MB). 126 GrowthStep(16 * datasize.MB). 127 Flags(func(f uint) uint { return f ^ mdbx1.Durable | mdbx1.SafeNoSync }). 128 SyncPeriod(2 * time.Second). 129 Open() 130 if err != nil { 131 return nil, err 132 } 133 // The nodes contained in the cache correspond to a certain protocol version. 134 // Flush all nodes if the version doesn't match. 135 currentVer := make([]byte, binary.MaxVarintLen64) 136 currentVer = currentVer[:binary.PutVarint(currentVer, int64(dbVersion))] 137 138 var blob []byte 139 if err := db.Update(context.Background(), func(tx kv.RwTx) error { 140 c, err := tx.RwCursor(kv.Inodes) 141 if err != nil { 142 return err 143 } 144 _, v, errGet := c.SeekExact([]byte(dbVersionKey)) 145 if errGet != nil { 146 return errGet 147 } 148 if v != nil { 149 // v only lives during transaction tx 150 blob = make([]byte, len(v)) 151 copy(blob, v) 152 return nil 153 } 154 return c.Put([]byte(dbVersionKey), currentVer) 155 }); err != nil { 156 return nil, err 157 } 158 if blob != nil && !bytes.Equal(blob, currentVer) { 159 db.Close() 160 if err := os.RemoveAll(path); err != nil { 161 return nil, err 162 } 163 return newPersistentDB(logger, path) 164 } 165 return &DB{kv: db, quit: make(chan struct{})}, nil 166 } 167 168 // nodeKey returns the database key for a node record. 169 func nodeKey(id ID) []byte { 170 key := append([]byte(dbNodePrefix), id[:]...) 171 key = append(key, ':') 172 key = append(key, dbDiscoverRoot...) 173 return key 174 } 175 176 // splitNodeKey returns the node ID of a key created by nodeKey. 177 func splitNodeKey(key []byte) (id ID, rest []byte) { 178 if !bytes.HasPrefix(key, []byte(dbNodePrefix)) { 179 return ID{}, nil 180 } 181 item := key[len(dbNodePrefix):] 182 copy(id[:], item[:len(id)]) 183 return id, item[len(id)+1:] 184 } 185 186 // nodeItemKey returns the database key for a node metadata field. 187 func nodeItemKey(id ID, ip net.IP, field string) []byte { 188 ip16 := ip.To16() 189 if ip16 == nil { 190 panic(fmt.Errorf("invalid IP (length %d)", len(ip))) 191 } 192 return bytes.Join([][]byte{nodeKey(id), ip16, []byte(field)}, []byte{':'}) 193 } 194 195 // splitNodeItemKey returns the components of a key created by nodeItemKey. 196 func splitNodeItemKey(key []byte) (id ID, ip net.IP, field string) { 197 id, key = splitNodeKey(key) 198 // Skip discover root. 199 if string(key) == dbDiscoverRoot { 200 return id, nil, "" 201 } 202 key = key[len(dbDiscoverRoot)+1:] 203 // Split out the IP. 204 ip = key[:16] 205 if ip4 := ip.To4(); ip4 != nil { 206 ip = ip4 207 } 208 key = key[16+1:] 209 // Field is the remainder of key. 210 field = string(key) 211 return id, ip, field 212 } 213 214 func v5Key(id ID, ip net.IP, field string) []byte { 215 return bytes.Join([][]byte{ 216 []byte(dbNodePrefix), 217 id[:], 218 []byte(dbDiscv5Root), 219 ip.To16(), 220 []byte(field), 221 }, []byte{':'}) 222 } 223 224 // localItemKey returns the key of a local node item. 225 func localItemKey(id ID, field string) []byte { 226 key := append([]byte(dbLocalPrefix), id[:]...) 227 key = append(key, ':') 228 key = append(key, field...) 229 return key 230 } 231 232 func copyBytes(key []byte) (copiedBytes []byte) { 233 if key == nil { 234 return nil 235 } 236 copiedBytes = make([]byte, len(key)) 237 copy(copiedBytes, key) 238 239 return 240 } 241 242 // fetchInt64 retrieves an integer associated with a particular key. 243 func (db *DB) fetchInt64(key []byte) int64 { 244 var val int64 245 if err := db.kv.View(context.Background(), func(tx kv.Tx) error { 246 blob, errGet := tx.GetOne(kv.Inodes, key) 247 if errGet != nil { 248 return errGet 249 } 250 if blob != nil { 251 if v, read := binary.Varint(blob); read > 0 { 252 val = v 253 } 254 } 255 return nil 256 }); err != nil { 257 return 0 258 } 259 260 return val 261 } 262 263 // storeInt64 stores an integer in the given key. 264 func (db *DB) storeInt64(key []byte, n int64) error { 265 blob := make([]byte, binary.MaxVarintLen64) 266 blob = blob[:binary.PutVarint(blob, n)] 267 return db.kv.Update(context.Background(), func(tx kv.RwTx) error { 268 return tx.Put(kv.Inodes, copyBytes(key), blob) 269 }) 270 } 271 272 // fetchUint64 retrieves an integer associated with a particular key. 273 func (db *DB) fetchUint64(key []byte) uint64 { 274 var val uint64 275 if err := db.kv.View(context.Background(), func(tx kv.Tx) error { 276 blob, errGet := tx.GetOne(kv.Inodes, key) 277 if errGet != nil { 278 return errGet 279 } 280 if blob != nil { 281 val, _ = binary.Uvarint(blob) 282 } 283 return nil 284 }); err != nil { 285 return 0 286 } 287 return val 288 } 289 290 // storeUint64 stores an integer in the given key. 291 func (db *DB) storeUint64(key []byte, n uint64) error { 292 blob := make([]byte, binary.MaxVarintLen64) 293 blob = blob[:binary.PutUvarint(blob, n)] 294 return db.kv.Update(context.Background(), func(tx kv.RwTx) error { 295 return tx.Put(kv.Inodes, copyBytes(key), blob) 296 }) 297 } 298 299 // Node retrieves a node with a given id from the database. 300 func (db *DB) Node(id ID) *Node { 301 var blob []byte 302 if err := db.kv.View(context.Background(), func(tx kv.Tx) error { 303 v, errGet := tx.GetOne(kv.NodeRecords, nodeKey(id)) 304 if errGet != nil { 305 return errGet 306 } 307 if v != nil { 308 blob = make([]byte, len(v)) 309 copy(blob, v) 310 } 311 return nil 312 }); err != nil { 313 return nil 314 } 315 if blob == nil { 316 return nil 317 } 318 return mustDecodeNode(id[:], blob) 319 } 320 321 func mustDecodeNode(id, data []byte) *Node { 322 node := new(Node) 323 if err := rlp.DecodeBytes(data, &node.r); err != nil { 324 panic(fmt.Errorf("p2p/enode: can't decode node %x in DB: %w", id, err)) 325 } 326 // Restore node id cache. 327 copy(node.id[:], id) 328 return node 329 } 330 331 // UpdateNode inserts - potentially overwriting - a node into the peer database. 332 func (db *DB) UpdateNode(node *Node) error { 333 if node.Seq() < db.NodeSeq(node.ID()) { 334 return nil 335 } 336 blob, err := rlp.EncodeToBytes(&node.r) 337 if err != nil { 338 return err 339 } 340 if err := db.kv.Update(context.Background(), func(tx kv.RwTx) error { 341 return tx.Put(kv.NodeRecords, nodeKey(node.ID()), blob) 342 }); err != nil { 343 return err 344 } 345 return db.storeUint64(nodeItemKey(node.ID(), zeroIP, dbNodeSeq), node.Seq()) 346 } 347 348 // NodeSeq returns the stored record sequence number of the given node. 349 func (db *DB) NodeSeq(id ID) uint64 { 350 return db.fetchUint64(nodeItemKey(id, zeroIP, dbNodeSeq)) 351 } 352 353 // Resolve returns the stored record of the node if it has a larger sequence 354 // number than n. 355 func (db *DB) Resolve(n *Node) *Node { 356 if n.Seq() > db.NodeSeq(n.ID()) { 357 return n 358 } 359 return db.Node(n.ID()) 360 } 361 362 // DeleteNode deletes all information associated with a node. 363 func (db *DB) DeleteNode(id ID) { 364 deleteRange(db.kv, nodeKey(id)) 365 } 366 367 func deleteRange(db kv.RwDB, prefix []byte) { 368 if err := db.Update(context.Background(), func(tx kv.RwTx) error { 369 for bucket := range bucketsConfig(nil) { 370 if err := deleteRangeInBucket(tx, prefix, bucket); err != nil { 371 return err 372 } 373 } 374 return nil 375 }); err != nil { 376 log.Warn("nodeDB.deleteRange failed", "err", err) 377 } 378 } 379 380 func deleteRangeInBucket(tx kv.RwTx, prefix []byte, bucket string) error { 381 c, err := tx.RwCursor(bucket) 382 if err != nil { 383 return err 384 } 385 var k []byte 386 for k, _, err = c.Seek(prefix); (err == nil) && (k != nil) && bytes.HasPrefix(k, prefix); k, _, err = c.Next() { 387 if err = c.DeleteCurrent(); err != nil { 388 break 389 } 390 } 391 return err 392 } 393 394 // ensureExpirer is a small helper method ensuring that the data expiration 395 // mechanism is running. If the expiration goroutine is already running, this 396 // method simply returns. 397 // 398 // The goal is to start the data evacuation only after the network successfully 399 // bootstrapped itself (to prevent dumping potentially useful seed nodes). Since 400 // it would require significant overhead to exactly trace the first successful 401 // convergence, it's simpler to "ensure" the correct state when an appropriate 402 // condition occurs (i.e. a successful bonding), and discard further events. 403 func (db *DB) ensureExpirer() { 404 db.runner.Do(func() { go db.expirer() }) 405 } 406 407 // expirer should be started in a go routine, and is responsible for looping ad 408 // infinitum and dropping stale data from the database. 409 func (db *DB) expirer() { 410 tick := time.NewTicker(dbCleanupCycle) 411 defer tick.Stop() 412 for { 413 select { 414 case <-tick.C: 415 db.expireNodes() 416 case <-db.quit: 417 return 418 } 419 } 420 } 421 422 // expireNodes iterates over the database and deletes all nodes that have not 423 // been seen (i.e. received a pong from) for some time. 424 func (db *DB) expireNodes() { 425 var ( 426 threshold = time.Now().Add(-dbNodeExpiration).Unix() 427 youngestPong int64 428 ) 429 var toDelete [][]byte 430 if err := db.kv.View(context.Background(), func(tx kv.Tx) error { 431 c, err := tx.Cursor(kv.Inodes) 432 if err != nil { 433 return err 434 } 435 p := []byte(dbNodePrefix) 436 var prevId ID 437 var empty = true 438 for k, v, err := c.Seek(p); bytes.HasPrefix(k, p); k, v, err = c.Next() { 439 if err != nil { 440 return err 441 } 442 id, ip, field := splitNodeItemKey(k) 443 if field == dbNodePong { 444 time, _ := binary.Varint(v) 445 if time > youngestPong { 446 youngestPong = time 447 } 448 if time < threshold { 449 // Last pong from this IP older than threshold, remove fields belonging to it. 450 toDelete = append(toDelete, nodeItemKey(id, ip, "")) 451 } 452 } 453 if id != prevId { 454 if youngestPong > 0 && youngestPong < threshold { 455 toDelete = append(toDelete, nodeKey(prevId)) 456 } 457 youngestPong = 0 458 } 459 prevId = id 460 empty = false 461 } 462 if !empty { 463 if youngestPong > 0 && youngestPong < threshold { 464 toDelete = append(toDelete, nodeKey(prevId)) 465 } 466 youngestPong = 0 467 } 468 return nil 469 }); err != nil { 470 log.Warn("nodeDB.expireNodes failed", "err", err) 471 } 472 for _, td := range toDelete { 473 deleteRange(db.kv, td) 474 } 475 } 476 477 // LastPingReceived retrieves the time of the last ping packet received from 478 // a remote node. 479 func (db *DB) LastPingReceived(id ID, ip net.IP) time.Time { 480 if ip = ip.To16(); ip == nil { 481 return time.Time{} 482 } 483 return time.Unix(db.fetchInt64(nodeItemKey(id, ip, dbNodePing)), 0) 484 } 485 486 // UpdateLastPingReceived updates the last time we tried contacting a remote node. 487 func (db *DB) UpdateLastPingReceived(id ID, ip net.IP, instance time.Time) error { 488 if ip = ip.To16(); ip == nil { 489 return errInvalidIP 490 } 491 return db.storeInt64(nodeItemKey(id, ip, dbNodePing), instance.Unix()) 492 } 493 494 // LastPongReceived retrieves the time of the last successful pong from remote node. 495 func (db *DB) LastPongReceived(id ID, ip net.IP) time.Time { 496 if ip = ip.To16(); ip == nil { 497 return time.Time{} 498 } 499 // Launch expirer 500 db.ensureExpirer() 501 return time.Unix(db.fetchInt64(nodeItemKey(id, ip, dbNodePong)), 0) 502 } 503 504 // UpdateLastPongReceived updates the last pong time of a node. 505 func (db *DB) UpdateLastPongReceived(id ID, ip net.IP, instance time.Time) error { 506 if ip = ip.To16(); ip == nil { 507 return errInvalidIP 508 } 509 return db.storeInt64(nodeItemKey(id, ip, dbNodePong), instance.Unix()) 510 } 511 512 // FindFails retrieves the number of findnode failures since bonding. 513 func (db *DB) FindFails(id ID, ip net.IP) int { 514 if ip = ip.To16(); ip == nil { 515 return 0 516 } 517 return int(db.fetchInt64(nodeItemKey(id, ip, dbNodeFindFails))) 518 } 519 520 // UpdateFindFails updates the number of findnode failures since bonding. 521 func (db *DB) UpdateFindFails(id ID, ip net.IP, fails int) error { 522 if ip = ip.To16(); ip == nil { 523 return errInvalidIP 524 } 525 return db.storeInt64(nodeItemKey(id, ip, dbNodeFindFails), int64(fails)) 526 } 527 528 // FindFailsV5 retrieves the discv5 findnode failure counter. 529 func (db *DB) FindFailsV5(id ID, ip net.IP) int { 530 if ip = ip.To16(); ip == nil { 531 return 0 532 } 533 return int(db.fetchInt64(v5Key(id, ip, dbNodeFindFails))) 534 } 535 536 // UpdateFindFailsV5 stores the discv5 findnode failure counter. 537 func (db *DB) UpdateFindFailsV5(id ID, ip net.IP, fails int) error { 538 if ip = ip.To16(); ip == nil { 539 return errInvalidIP 540 } 541 return db.storeInt64(v5Key(id, ip, dbNodeFindFails), int64(fails)) 542 } 543 544 // LocalSeq retrieves the local record sequence counter. 545 func (db *DB) localSeq(id ID) uint64 { 546 return db.fetchUint64(localItemKey(id, dbLocalSeq)) 547 } 548 549 // storeLocalSeq stores the local record sequence counter. 550 func (db *DB) storeLocalSeq(id ID, n uint64) { 551 db.storeUint64(localItemKey(id, dbLocalSeq), n) 552 } 553 554 // QuerySeeds retrieves random nodes to be used as potential seed nodes 555 // for bootstrapping. 556 func (db *DB) QuerySeeds(n int, maxAge time.Duration) []*Node { 557 var ( 558 now = time.Now() 559 nodes = make([]*Node, 0, n) 560 id ID 561 ) 562 563 if err := db.kv.View(context.Background(), func(tx kv.Tx) error { 564 c, err := tx.Cursor(kv.NodeRecords) 565 if err != nil { 566 return err 567 } 568 seek: 569 for seeks := 0; len(nodes) < n && seeks < n*5; seeks++ { 570 // Seek to a random entry. The first byte is incremented by a 571 // random amount each time in order to increase the likelihood 572 // of hitting all existing nodes in very small databases. 573 ctr := id[0] 574 rand.Read(id[:]) 575 id[0] = ctr + id[0]%16 576 var n *Node 577 for k, v, err := c.Seek(nodeKey(id)); k != nil && n == nil; k, v, err = c.Next() { 578 if err != nil { 579 return err 580 } 581 id, rest := splitNodeKey(k) 582 if string(rest) == dbDiscoverRoot { 583 n = mustDecodeNode(id[:], v) 584 } 585 } 586 if n == nil { 587 id[0] = 0 588 continue // iterator exhausted 589 } 590 db.ensureExpirer() 591 pongKey := nodeItemKey(n.ID(), n.IP(), dbNodePong) 592 var lastPongReceived int64 593 blob, errGet := tx.GetOne(kv.Inodes, pongKey) 594 if errGet != nil { 595 return errGet 596 } 597 if blob != nil { 598 if v, read := binary.Varint(blob); read > 0 { 599 lastPongReceived = v 600 } 601 } 602 if now.Sub(time.Unix(lastPongReceived, 0)) > maxAge { 603 continue 604 } 605 for i := range nodes { 606 if nodes[i].ID() == n.ID() { 607 continue seek // duplicate 608 } 609 } 610 nodes = append(nodes, n) 611 } 612 return nil 613 }); err != nil { 614 log.Warn("nodeDB.QuerySeeds failed", "err", err) 615 } 616 return nodes 617 } 618 619 // close flushes and closes the database files. 620 func (db *DB) Close() { 621 select { 622 case <-db.quit: 623 return // means closed already 624 default: 625 } 626 if db.quit == nil { 627 return 628 } 629 libcommon.SafeClose(db.quit) 630 db.kv.Close() 631 }