github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/consensus/istanbul/backend/internal/enodes/val_enode_db.go (about)

     1  // Copyright 2017 The Celo Authors
     2  // This file is part of the celo library.
     3  //
     4  // The celo 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 celo 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 celo library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package enodes
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/binary"
    22  	"fmt"
    23  	"io"
    24  	"os"
    25  	"strings"
    26  	"sync"
    27  
    28  	"github.com/syndtr/goleveldb/leveldb"
    29  	lvlerrors "github.com/syndtr/goleveldb/leveldb/errors"
    30  	"github.com/syndtr/goleveldb/leveldb/opt"
    31  	"github.com/syndtr/goleveldb/leveldb/storage"
    32  	"github.com/syndtr/goleveldb/leveldb/util"
    33  
    34  	"github.com/ethereum/go-ethereum/common"
    35  	"github.com/ethereum/go-ethereum/consensus/istanbul"
    36  	"github.com/ethereum/go-ethereum/log"
    37  	"github.com/ethereum/go-ethereum/p2p/enode"
    38  	"github.com/ethereum/go-ethereum/rlp"
    39  )
    40  
    41  // Keys in the node database.
    42  const (
    43  	dbVersionKey    = "version"  // Version of the database to flush if changes
    44  	dbAddressPrefix = "address:" // Identifier to prefix node entries with
    45  	dbNodeIDPrefix  = "nodeid:"
    46  )
    47  
    48  const (
    49  	// dbNodeExpiration = 24 * time.Hour // Time after which an unseen node should be dropped.
    50  	// dbCleanupCycle   = time.Hour      // Time period for running the expiration task.
    51  	dbVersion = 2
    52  )
    53  
    54  // ValidatorEnodeHandler is handler to Add/Remove events. Events execute within write lock
    55  type ValidatorEnodeHandler interface {
    56  	// AddValidatorPeer adds a validator peer
    57  	AddValidatorPeer(node *enode.Node, address common.Address)
    58  
    59  	// RemoveValidatorPeer removes a validator peer
    60  	RemoveValidatorPeer(node *enode.Node)
    61  
    62  	// ReplaceValidatorPeers replace all validator peers for new list of enodeURLs
    63  	ReplaceValidatorPeers(newNodes []*enode.Node)
    64  
    65  	// Clear all validator peers
    66  	ClearValidatorPeers()
    67  }
    68  
    69  func addressKey(address common.Address) []byte {
    70  	return append([]byte(dbAddressPrefix), address.Bytes()...)
    71  }
    72  
    73  func nodeIDKey(nodeID enode.ID) []byte {
    74  	return append([]byte(dbNodeIDPrefix), nodeID.Bytes()...)
    75  }
    76  
    77  // AddressEntry is an entry for the valEnodeTable
    78  type AddressEntry struct {
    79  	Node      *enode.Node
    80  	Timestamp uint
    81  }
    82  
    83  func (ve *AddressEntry) String() string {
    84  	return fmt.Sprintf("{enodeURL: %v, timestamp: %v}", ve.Node.String(), ve.Timestamp)
    85  }
    86  
    87  // Implement RLP Encode/Decode interface
    88  type rlpEntry struct {
    89  	EnodeURL  string
    90  	Timestamp uint
    91  }
    92  
    93  // EncodeRLP serializes AddressEntry into the Ethereum RLP format.
    94  func (ve *AddressEntry) EncodeRLP(w io.Writer) error {
    95  	return rlp.Encode(w, rlpEntry{ve.Node.String(), ve.Timestamp})
    96  }
    97  
    98  // DecodeRLP implements rlp.Decoder, and load the AddressEntry fields from a RLP stream.
    99  func (ve *AddressEntry) DecodeRLP(s *rlp.Stream) error {
   100  	var entry rlpEntry
   101  	if err := s.Decode(&entry); err != nil {
   102  		return err
   103  	}
   104  
   105  	node, err := enode.ParseV4(entry.EnodeURL)
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	*ve = AddressEntry{Node: node, Timestamp: entry.Timestamp}
   111  	return nil
   112  }
   113  
   114  // ValidatorEnodeDB represents a Map that can be accessed either
   115  // by address or enode
   116  type ValidatorEnodeDB struct {
   117  	db      *leveldb.DB //the actual DB
   118  	lock    sync.RWMutex
   119  	handler ValidatorEnodeHandler
   120  	logger  log.Logger
   121  }
   122  
   123  // OpenValidatorEnodeDB opens a validator enode database for storing and retrieving infos about validator
   124  // enodes. If no path is given an in-memory, temporary database is constructed.
   125  func OpenValidatorEnodeDB(path string, handler ValidatorEnodeHandler) (*ValidatorEnodeDB, error) {
   126  	var db *leveldb.DB
   127  	var err error
   128  
   129  	logger := log.New()
   130  
   131  	if path == "" {
   132  		db, err = newMemoryDB()
   133  	} else {
   134  		db, err = newPersistentDB(path, logger)
   135  	}
   136  
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  	return &ValidatorEnodeDB{
   141  		db:      db,
   142  		handler: handler,
   143  		logger:  logger,
   144  	}, nil
   145  }
   146  
   147  // newMemoryDB creates a new in-memory node database without a persistent backend.
   148  func newMemoryDB() (*leveldb.DB, error) {
   149  	db, err := leveldb.Open(storage.NewMemStorage(), nil)
   150  	if err != nil {
   151  		return nil, err
   152  	}
   153  	return db, nil
   154  }
   155  
   156  // newPersistentNodeDB creates/opens a leveldb backed persistent node database,
   157  // also flushing its contents in case of a version mismatch.
   158  func newPersistentDB(path string, logger log.Logger) (*leveldb.DB, error) {
   159  	opts := &opt.Options{OpenFilesCacheCapacity: 5}
   160  	db, err := leveldb.OpenFile(path, opts)
   161  	if _, iscorrupted := err.(*lvlerrors.ErrCorrupted); iscorrupted {
   162  		db, err = leveldb.RecoverFile(path, nil)
   163  	}
   164  	if err != nil {
   165  		return nil, err
   166  	}
   167  	// The nodes contained in the cache correspond to a certain protocol version.
   168  	// Flush all nodes if the version doesn't match.
   169  	currentVer := make([]byte, binary.MaxVarintLen64)
   170  	currentVer = currentVer[:binary.PutVarint(currentVer, int64(dbVersion))]
   171  
   172  	blob, err := db.Get([]byte(dbVersionKey), nil)
   173  	switch err {
   174  	case leveldb.ErrNotFound:
   175  		// Version not found (i.e. empty cache), insert it
   176  		if err := db.Put([]byte(dbVersionKey), currentVer, nil); err != nil {
   177  			db.Close()
   178  			return nil, err
   179  		}
   180  
   181  	case nil:
   182  		// Version present, flush if different
   183  		if !bytes.Equal(blob, currentVer) {
   184  			oldVersion, _ := binary.Varint(blob)
   185  			newVersion, _ := binary.Varint(currentVer)
   186  			logger.Info("Val Enode DB version has changed.  Creating a new leveldb.", "old version", oldVersion, "new version", newVersion)
   187  			db.Close()
   188  			if err = os.RemoveAll(path); err != nil {
   189  				return nil, err
   190  			}
   191  			return newPersistentDB(path, logger)
   192  		}
   193  	}
   194  	return db, nil
   195  }
   196  
   197  // Close flushes and closes the database files.
   198  func (vet *ValidatorEnodeDB) Close() error {
   199  	return vet.db.Close()
   200  }
   201  
   202  func (vet *ValidatorEnodeDB) String() string {
   203  	vet.lock.RLock()
   204  	defer vet.lock.RUnlock()
   205  	var b strings.Builder
   206  	b.WriteString("ValEnodeTable:")
   207  
   208  	err := vet.iterateOverAddressEntries(func(address common.Address, entry *AddressEntry) error {
   209  		fmt.Fprintf(&b, " [%s => %s]", address.String(), entry.String())
   210  		return nil
   211  	})
   212  
   213  	if err != nil {
   214  		vet.logger.Error("ValidatorEnodeDB.String error", "err", err)
   215  	}
   216  
   217  	return b.String()
   218  }
   219  
   220  // GetEnodeURLFromAddress will return the enodeURL for an address if it's known
   221  func (vet *ValidatorEnodeDB) GetNodeFromAddress(address common.Address) (*enode.Node, error) {
   222  	vet.lock.RLock()
   223  	defer vet.lock.RUnlock()
   224  	entry, err := vet.getAddressEntry(address)
   225  	if err != nil {
   226  		return nil, err
   227  	}
   228  	return entry.Node, nil
   229  }
   230  
   231  // GetTimestampFromAddress will return the timestamp for an address if it's known
   232  func (vet *ValidatorEnodeDB) GetTimestampFromAddress(address common.Address) (uint, error) {
   233  	vet.lock.RLock()
   234  	defer vet.lock.RUnlock()
   235  	entry, err := vet.getAddressEntry(address)
   236  	if err != nil {
   237  		return 0, err
   238  	}
   239  	return entry.Timestamp, nil
   240  }
   241  
   242  // GetAddressFromNodeID will return the address for an nodeID if it's known
   243  func (vet *ValidatorEnodeDB) GetAddressFromNodeID(nodeID enode.ID) (common.Address, error) {
   244  	vet.lock.RLock()
   245  	defer vet.lock.RUnlock()
   246  
   247  	rawEntry, err := vet.db.Get(nodeIDKey(nodeID), nil)
   248  	if err != nil {
   249  		return common.ZeroAddress, err
   250  	}
   251  	return common.BytesToAddress(rawEntry), nil
   252  }
   253  
   254  // GetAllValEnodes will return all entries in the valEnodeDB
   255  func (vet *ValidatorEnodeDB) GetAllValEnodes() (map[common.Address]*AddressEntry, error) {
   256  	vet.lock.RLock()
   257  	defer vet.lock.RUnlock()
   258  	var entries = make(map[common.Address]*AddressEntry)
   259  
   260  	err := vet.iterateOverAddressEntries(func(address common.Address, entry *AddressEntry) error {
   261  		entries[address] = entry
   262  		return nil
   263  	})
   264  
   265  	if err != nil {
   266  		vet.logger.Error("ValidatorEnodeDB.GetAllAddressEntries error", "err", err)
   267  		return nil, err
   268  	}
   269  
   270  	return entries, nil
   271  }
   272  
   273  // Upsert will update or insert a validator enode entry; given that the existing entry
   274  // is older (determined by timestamp parameter) than the new one
   275  // TODO - In addition to modifying the val_enode_db, this function also will disconnect
   276  //        and/or connect the corresponding validator connenctions.  The validator connections
   277  //        should be managed be a separate thread (see https://github.com/celo-org/celo-blockchain/issues/607)
   278  func (vet *ValidatorEnodeDB) Upsert(valEnodeEntries map[common.Address]*AddressEntry) error {
   279  	vet.lock.Lock()
   280  	defer vet.lock.Unlock()
   281  
   282  	batch := new(leveldb.Batch)
   283  	peersToRemove := make([]*enode.Node, 0, len(valEnodeEntries))
   284  	peersToAdd := make(map[common.Address]*enode.Node)
   285  
   286  	for remoteAddress, addressEntry := range valEnodeEntries {
   287  		currentEntry, err := vet.getAddressEntry(remoteAddress)
   288  		isNew := err == leveldb.ErrNotFound
   289  
   290  		// Check errors
   291  		if !isNew && err != nil {
   292  			return err
   293  		}
   294  
   295  		// new entry
   296  		rawEntry, err := rlp.EncodeToBytes(addressEntry)
   297  		if err != nil {
   298  			return err
   299  		}
   300  
   301  		// If it's an old message, ignore it
   302  		if !isNew && addressEntry.Timestamp < currentEntry.Timestamp {
   303  			vet.logger.Trace("Ignoring the entry because its timestamp is older than what is stored in the val enode db",
   304  				"entryAddress", remoteAddress, "newEntry", addressEntry.String(), "currentEntry", currentEntry.String())
   305  			continue
   306  		}
   307  
   308  		hasOldValueChanged := !isNew && currentEntry.Node.String() != addressEntry.Node.String()
   309  
   310  		if hasOldValueChanged {
   311  			batch.Delete(nodeIDKey(currentEntry.Node.ID()))
   312  			batch.Put(nodeIDKey(addressEntry.Node.ID()), remoteAddress.Bytes())
   313  			peersToRemove = append(peersToRemove, currentEntry.Node)
   314  		} else if isNew {
   315  			batch.Put(nodeIDKey(addressEntry.Node.ID()), remoteAddress.Bytes())
   316  		}
   317  		batch.Put(addressKey(remoteAddress), rawEntry)
   318  		peersToAdd[remoteAddress] = addressEntry.Node
   319  
   320  		vet.logger.Trace("Going to upsert an entry in the valEnodeTable", "address", remoteAddress, "enodeURL", addressEntry.Node.String())
   321  	}
   322  
   323  	if batch.Len() > 0 {
   324  		err := vet.db.Write(batch, nil)
   325  		if err != nil {
   326  			return err
   327  		} else {
   328  			for _, node := range peersToRemove {
   329  				vet.handler.RemoveValidatorPeer(node)
   330  			}
   331  
   332  			for address, node := range peersToAdd {
   333  				vet.handler.AddValidatorPeer(node, address)
   334  			}
   335  		}
   336  	}
   337  
   338  	return nil
   339  }
   340  
   341  // RemoveEntry will remove an entry from the table
   342  func (vet *ValidatorEnodeDB) RemoveEntry(address common.Address) error {
   343  	vet.lock.Lock()
   344  	defer vet.lock.Unlock()
   345  	batch := new(leveldb.Batch)
   346  	err := vet.addDeleteToBatch(batch, address)
   347  	if err != nil {
   348  		return err
   349  	}
   350  	return vet.db.Write(batch, nil)
   351  }
   352  
   353  // PruneEntries will remove entries for all address not present in addressesToKeep
   354  func (vet *ValidatorEnodeDB) PruneEntries(addressesToKeep map[common.Address]bool) error {
   355  	vet.lock.Lock()
   356  	defer vet.lock.Unlock()
   357  	batch := new(leveldb.Batch)
   358  	err := vet.iterateOverAddressEntries(func(address common.Address, entry *AddressEntry) error {
   359  		if !addressesToKeep[address] {
   360  			vet.logger.Trace("Deleting entry from valEnodeTable", "address", address)
   361  			return vet.addDeleteToBatch(batch, address)
   362  		}
   363  		return nil
   364  	})
   365  
   366  	if err != nil {
   367  		return err
   368  	}
   369  	return vet.db.Write(batch, nil)
   370  }
   371  
   372  func (vet *ValidatorEnodeDB) RefreshValPeers(valset istanbul.ValidatorSet, ourAddress common.Address) {
   373  	// We use a R lock since we don't modify levelDB table
   374  	vet.lock.RLock()
   375  	defer vet.lock.RUnlock()
   376  
   377  	if valset.ContainsByAddress(ourAddress) {
   378  		// transform address to enodeURLs
   379  		newNodes := []*enode.Node{}
   380  		for _, val := range valset.List() {
   381  			entry, err := vet.getAddressEntry(val.Address())
   382  			if err == nil {
   383  				newNodes = append(newNodes, entry.Node)
   384  			} else if err != leveldb.ErrNotFound {
   385  				vet.logger.Error("Error reading valEnodeTable: GetEnodeURLFromAddress", "err", err)
   386  			}
   387  		}
   388  
   389  		vet.handler.ReplaceValidatorPeers(newNodes)
   390  	} else {
   391  		// Disconnect all validator peers if this node is not in the valset
   392  		vet.handler.ClearValidatorPeers()
   393  	}
   394  }
   395  
   396  func (vet *ValidatorEnodeDB) addDeleteToBatch(batch *leveldb.Batch, address common.Address) error {
   397  	entry, err := vet.getAddressEntry(address)
   398  	if err != nil {
   399  		return err
   400  	}
   401  
   402  	batch.Delete(addressKey(address))
   403  	batch.Delete(nodeIDKey(entry.Node.ID()))
   404  	if vet.handler != nil {
   405  		vet.handler.RemoveValidatorPeer(entry.Node)
   406  	}
   407  	return nil
   408  }
   409  
   410  func (vet *ValidatorEnodeDB) getAddressEntry(address common.Address) (*AddressEntry, error) {
   411  	var entry AddressEntry
   412  	rawEntry, err := vet.db.Get(addressKey(address), nil)
   413  	if err != nil {
   414  		return nil, err
   415  	}
   416  
   417  	if err = rlp.DecodeBytes(rawEntry, &entry); err != nil {
   418  		return nil, err
   419  	}
   420  	return &entry, nil
   421  }
   422  
   423  func (vet *ValidatorEnodeDB) iterateOverAddressEntries(onEntry func(common.Address, *AddressEntry) error) error {
   424  	iter := vet.db.NewIterator(util.BytesPrefix([]byte(dbAddressPrefix)), nil)
   425  	defer iter.Release()
   426  
   427  	for iter.Next() {
   428  		var entry AddressEntry
   429  		address := common.BytesToAddress(iter.Key()[len(dbAddressPrefix):])
   430  		rlp.DecodeBytes(iter.Value(), &entry)
   431  
   432  		err := onEntry(address, &entry)
   433  		if err != nil {
   434  			return err
   435  		}
   436  	}
   437  	return iter.Error()
   438  }
   439  
   440  type ValEnodeEntryInfo struct {
   441  	Enode     string `json:"enode"`
   442  	Timestamp uint   `json:"timestamp"`
   443  }
   444  
   445  func (vet *ValidatorEnodeDB) ValEnodeTableInfo() (map[string]*ValEnodeEntryInfo, error) {
   446  	valEnodeTableInfo := make(map[string]*ValEnodeEntryInfo)
   447  
   448  	valEnodeTable, err := vet.GetAllValEnodes()
   449  	if err == nil {
   450  		for address, valEnodeEntry := range valEnodeTable {
   451  			valEnodeTableInfo[address.Hex()] = &ValEnodeEntryInfo{Enode: valEnodeEntry.Node.String(),
   452  				Timestamp: valEnodeEntry.Timestamp}
   453  		}
   454  	}
   455  
   456  	return valEnodeTableInfo, err
   457  }