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