github.com/ethereum/go-ethereum@v1.14.3/core/rawdb/database.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 rawdb
    18  
    19  import (
    20  	"bytes"
    21  	"errors"
    22  	"fmt"
    23  	"os"
    24  	"path/filepath"
    25  	"strings"
    26  	"time"
    27  
    28  	"github.com/ethereum/go-ethereum/common"
    29  	"github.com/ethereum/go-ethereum/ethdb"
    30  	"github.com/ethereum/go-ethereum/ethdb/leveldb"
    31  	"github.com/ethereum/go-ethereum/ethdb/memorydb"
    32  	"github.com/ethereum/go-ethereum/ethdb/pebble"
    33  	"github.com/ethereum/go-ethereum/log"
    34  	"github.com/olekukonko/tablewriter"
    35  )
    36  
    37  // freezerdb is a database wrapper that enables ancient chain segment freezing.
    38  type freezerdb struct {
    39  	ethdb.KeyValueStore
    40  	*chainFreezer
    41  
    42  	readOnly    bool
    43  	ancientRoot string
    44  }
    45  
    46  // AncientDatadir returns the path of root ancient directory.
    47  func (frdb *freezerdb) AncientDatadir() (string, error) {
    48  	return frdb.ancientRoot, nil
    49  }
    50  
    51  // Close implements io.Closer, closing both the fast key-value store as well as
    52  // the slow ancient tables.
    53  func (frdb *freezerdb) Close() error {
    54  	var errs []error
    55  	if err := frdb.chainFreezer.Close(); err != nil {
    56  		errs = append(errs, err)
    57  	}
    58  	if err := frdb.KeyValueStore.Close(); err != nil {
    59  		errs = append(errs, err)
    60  	}
    61  	if len(errs) != 0 {
    62  		return fmt.Errorf("%v", errs)
    63  	}
    64  	return nil
    65  }
    66  
    67  // Freeze is a helper method used for external testing to trigger and block until
    68  // a freeze cycle completes, without having to sleep for a minute to trigger the
    69  // automatic background run.
    70  func (frdb *freezerdb) Freeze() error {
    71  	if frdb.readOnly {
    72  		return errReadOnly
    73  	}
    74  	// Trigger a freeze cycle and block until it's done
    75  	trigger := make(chan struct{}, 1)
    76  	frdb.chainFreezer.trigger <- trigger
    77  	<-trigger
    78  	return nil
    79  }
    80  
    81  // nofreezedb is a database wrapper that disables freezer data retrievals.
    82  type nofreezedb struct {
    83  	ethdb.KeyValueStore
    84  }
    85  
    86  // HasAncient returns an error as we don't have a backing chain freezer.
    87  func (db *nofreezedb) HasAncient(kind string, number uint64) (bool, error) {
    88  	return false, errNotSupported
    89  }
    90  
    91  // Ancient returns an error as we don't have a backing chain freezer.
    92  func (db *nofreezedb) Ancient(kind string, number uint64) ([]byte, error) {
    93  	return nil, errNotSupported
    94  }
    95  
    96  // AncientRange returns an error as we don't have a backing chain freezer.
    97  func (db *nofreezedb) AncientRange(kind string, start, max, maxByteSize uint64) ([][]byte, error) {
    98  	return nil, errNotSupported
    99  }
   100  
   101  // Ancients returns an error as we don't have a backing chain freezer.
   102  func (db *nofreezedb) Ancients() (uint64, error) {
   103  	return 0, errNotSupported
   104  }
   105  
   106  // Tail returns an error as we don't have a backing chain freezer.
   107  func (db *nofreezedb) Tail() (uint64, error) {
   108  	return 0, errNotSupported
   109  }
   110  
   111  // AncientSize returns an error as we don't have a backing chain freezer.
   112  func (db *nofreezedb) AncientSize(kind string) (uint64, error) {
   113  	return 0, errNotSupported
   114  }
   115  
   116  // ModifyAncients is not supported.
   117  func (db *nofreezedb) ModifyAncients(func(ethdb.AncientWriteOp) error) (int64, error) {
   118  	return 0, errNotSupported
   119  }
   120  
   121  // TruncateHead returns an error as we don't have a backing chain freezer.
   122  func (db *nofreezedb) TruncateHead(items uint64) (uint64, error) {
   123  	return 0, errNotSupported
   124  }
   125  
   126  // TruncateTail returns an error as we don't have a backing chain freezer.
   127  func (db *nofreezedb) TruncateTail(items uint64) (uint64, error) {
   128  	return 0, errNotSupported
   129  }
   130  
   131  // Sync returns an error as we don't have a backing chain freezer.
   132  func (db *nofreezedb) Sync() error {
   133  	return errNotSupported
   134  }
   135  
   136  func (db *nofreezedb) ReadAncients(fn func(reader ethdb.AncientReaderOp) error) (err error) {
   137  	// Unlike other ancient-related methods, this method does not return
   138  	// errNotSupported when invoked.
   139  	// The reason for this is that the caller might want to do several things:
   140  	// 1. Check if something is in the freezer,
   141  	// 2. If not, check leveldb.
   142  	//
   143  	// This will work, since the ancient-checks inside 'fn' will return errors,
   144  	// and the leveldb work will continue.
   145  	//
   146  	// If we instead were to return errNotSupported here, then the caller would
   147  	// have to explicitly check for that, having an extra clause to do the
   148  	// non-ancient operations.
   149  	return fn(db)
   150  }
   151  
   152  // MigrateTable processes the entries in a given table in sequence
   153  // converting them to a new format if they're of an old format.
   154  func (db *nofreezedb) MigrateTable(kind string, convert convertLegacyFn) error {
   155  	return errNotSupported
   156  }
   157  
   158  // AncientDatadir returns an error as we don't have a backing chain freezer.
   159  func (db *nofreezedb) AncientDatadir() (string, error) {
   160  	return "", errNotSupported
   161  }
   162  
   163  // NewDatabase creates a high level database on top of a given key-value data
   164  // store without a freezer moving immutable chain segments into cold storage.
   165  func NewDatabase(db ethdb.KeyValueStore) ethdb.Database {
   166  	return &nofreezedb{KeyValueStore: db}
   167  }
   168  
   169  // resolveChainFreezerDir is a helper function which resolves the absolute path
   170  // of chain freezer by considering backward compatibility.
   171  func resolveChainFreezerDir(ancient string) string {
   172  	// Check if the chain freezer is already present in the specified
   173  	// sub folder, if not then two possibilities:
   174  	// - chain freezer is not initialized
   175  	// - chain freezer exists in legacy location (root ancient folder)
   176  	freezer := filepath.Join(ancient, ChainFreezerName)
   177  	if !common.FileExist(freezer) {
   178  		if !common.FileExist(ancient) {
   179  			// The entire ancient store is not initialized, still use the sub
   180  			// folder for initialization.
   181  		} else {
   182  			// Ancient root is already initialized, then we hold the assumption
   183  			// that chain freezer is also initialized and located in root folder.
   184  			// In this case fallback to legacy location.
   185  			freezer = ancient
   186  			log.Info("Found legacy ancient chain path", "location", ancient)
   187  		}
   188  	}
   189  	return freezer
   190  }
   191  
   192  // NewDatabaseWithFreezer creates a high level database on top of a given key-
   193  // value data store with a freezer moving immutable chain segments into cold
   194  // storage. The passed ancient indicates the path of root ancient directory
   195  // where the chain freezer can be opened.
   196  func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace string, readonly bool) (ethdb.Database, error) {
   197  	// Create the idle freezer instance. If the given ancient directory is empty,
   198  	// in-memory chain freezer is used (e.g. dev mode); otherwise the regular
   199  	// file-based freezer is created.
   200  	chainFreezerDir := ancient
   201  	if chainFreezerDir != "" {
   202  		chainFreezerDir = resolveChainFreezerDir(chainFreezerDir)
   203  	}
   204  	frdb, err := newChainFreezer(chainFreezerDir, namespace, readonly)
   205  	if err != nil {
   206  		printChainMetadata(db)
   207  		return nil, err
   208  	}
   209  	// Since the freezer can be stored separately from the user's key-value database,
   210  	// there's a fairly high probability that the user requests invalid combinations
   211  	// of the freezer and database. Ensure that we don't shoot ourselves in the foot
   212  	// by serving up conflicting data, leading to both datastores getting corrupted.
   213  	//
   214  	//   - If both the freezer and key-value store are empty (no genesis), we just
   215  	//     initialized a new empty freezer, so everything's fine.
   216  	//   - If the key-value store is empty, but the freezer is not, we need to make
   217  	//     sure the user's genesis matches the freezer. That will be checked in the
   218  	//     blockchain, since we don't have the genesis block here (nor should we at
   219  	//     this point care, the key-value/freezer combo is valid).
   220  	//   - If neither the key-value store nor the freezer is empty, cross validate
   221  	//     the genesis hashes to make sure they are compatible. If they are, also
   222  	//     ensure that there's no gap between the freezer and subsequently leveldb.
   223  	//   - If the key-value store is not empty, but the freezer is, we might just be
   224  	//     upgrading to the freezer release, or we might have had a small chain and
   225  	//     not frozen anything yet. Ensure that no blocks are missing yet from the
   226  	//     key-value store, since that would mean we already had an old freezer.
   227  
   228  	// If the genesis hash is empty, we have a new key-value store, so nothing to
   229  	// validate in this method. If, however, the genesis hash is not nil, compare
   230  	// it to the freezer content.
   231  	if kvgenesis, _ := db.Get(headerHashKey(0)); len(kvgenesis) > 0 {
   232  		if frozen, _ := frdb.Ancients(); frozen > 0 {
   233  			// If the freezer already contains something, ensure that the genesis blocks
   234  			// match, otherwise we might mix up freezers across chains and destroy both
   235  			// the freezer and the key-value store.
   236  			frgenesis, err := frdb.Ancient(ChainFreezerHashTable, 0)
   237  			if err != nil {
   238  				printChainMetadata(db)
   239  				return nil, fmt.Errorf("failed to retrieve genesis from ancient %v", err)
   240  			} else if !bytes.Equal(kvgenesis, frgenesis) {
   241  				printChainMetadata(db)
   242  				return nil, fmt.Errorf("genesis mismatch: %#x (leveldb) != %#x (ancients)", kvgenesis, frgenesis)
   243  			}
   244  			// Key-value store and freezer belong to the same network. Ensure that they
   245  			// are contiguous, otherwise we might end up with a non-functional freezer.
   246  			if kvhash, _ := db.Get(headerHashKey(frozen)); len(kvhash) == 0 {
   247  				// Subsequent header after the freezer limit is missing from the database.
   248  				// Reject startup if the database has a more recent head.
   249  				if head := *ReadHeaderNumber(db, ReadHeadHeaderHash(db)); head > frozen-1 {
   250  					// Find the smallest block stored in the key-value store
   251  					// in range of [frozen, head]
   252  					var number uint64
   253  					for number = frozen; number <= head; number++ {
   254  						if present, _ := db.Has(headerHashKey(number)); present {
   255  							break
   256  						}
   257  					}
   258  					// We are about to exit on error. Print database metadata before exiting
   259  					printChainMetadata(db)
   260  					return nil, fmt.Errorf("gap in the chain between ancients [0 - #%d] and leveldb [#%d - #%d] ",
   261  						frozen-1, number, head)
   262  				}
   263  				// Database contains only older data than the freezer, this happens if the
   264  				// state was wiped and reinited from an existing freezer.
   265  			}
   266  			// Otherwise, key-value store continues where the freezer left off, all is fine.
   267  			// We might have duplicate blocks (crash after freezer write but before key-value
   268  			// store deletion, but that's fine).
   269  		} else {
   270  			// If the freezer is empty, ensure nothing was moved yet from the key-value
   271  			// store, otherwise we'll end up missing data. We check block #1 to decide
   272  			// if we froze anything previously or not, but do take care of databases with
   273  			// only the genesis block.
   274  			if ReadHeadHeaderHash(db) != common.BytesToHash(kvgenesis) {
   275  				// Key-value store contains more data than the genesis block, make sure we
   276  				// didn't freeze anything yet.
   277  				if kvblob, _ := db.Get(headerHashKey(1)); len(kvblob) == 0 {
   278  					printChainMetadata(db)
   279  					return nil, errors.New("ancient chain segments already extracted, please set --datadir.ancient to the correct path")
   280  				}
   281  				// Block #1 is still in the database, we're allowed to init a new freezer
   282  			}
   283  			// Otherwise, the head header is still the genesis, we're allowed to init a new
   284  			// freezer.
   285  		}
   286  	}
   287  	// Freezer is consistent with the key-value database, permit combining the two
   288  	if !readonly {
   289  		frdb.wg.Add(1)
   290  		go func() {
   291  			frdb.freeze(db)
   292  			frdb.wg.Done()
   293  		}()
   294  	}
   295  	return &freezerdb{
   296  		ancientRoot:   ancient,
   297  		KeyValueStore: db,
   298  		chainFreezer:  frdb,
   299  	}, nil
   300  }
   301  
   302  // NewMemoryDatabase creates an ephemeral in-memory key-value database without a
   303  // freezer moving immutable chain segments into cold storage.
   304  func NewMemoryDatabase() ethdb.Database {
   305  	return NewDatabase(memorydb.New())
   306  }
   307  
   308  // NewMemoryDatabaseWithCap creates an ephemeral in-memory key-value database
   309  // with an initial starting capacity, but without a freezer moving immutable
   310  // chain segments into cold storage.
   311  func NewMemoryDatabaseWithCap(size int) ethdb.Database {
   312  	return NewDatabase(memorydb.NewWithCap(size))
   313  }
   314  
   315  // NewLevelDBDatabase creates a persistent key-value database without a freezer
   316  // moving immutable chain segments into cold storage.
   317  func NewLevelDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) {
   318  	db, err := leveldb.New(file, cache, handles, namespace, readonly)
   319  	if err != nil {
   320  		return nil, err
   321  	}
   322  	log.Info("Using LevelDB as the backing database")
   323  	return NewDatabase(db), nil
   324  }
   325  
   326  // NewPebbleDBDatabase creates a persistent key-value database without a freezer
   327  // moving immutable chain segments into cold storage.
   328  func NewPebbleDBDatabase(file string, cache int, handles int, namespace string, readonly, ephemeral bool) (ethdb.Database, error) {
   329  	db, err := pebble.New(file, cache, handles, namespace, readonly, ephemeral)
   330  	if err != nil {
   331  		return nil, err
   332  	}
   333  	return NewDatabase(db), nil
   334  }
   335  
   336  const (
   337  	dbPebble  = "pebble"
   338  	dbLeveldb = "leveldb"
   339  )
   340  
   341  // PreexistingDatabase checks the given data directory whether a database is already
   342  // instantiated at that location, and if so, returns the type of database (or the
   343  // empty string).
   344  func PreexistingDatabase(path string) string {
   345  	if _, err := os.Stat(filepath.Join(path, "CURRENT")); err != nil {
   346  		return "" // No pre-existing db
   347  	}
   348  	if matches, err := filepath.Glob(filepath.Join(path, "OPTIONS*")); len(matches) > 0 || err != nil {
   349  		if err != nil {
   350  			panic(err) // only possible if the pattern is malformed
   351  		}
   352  		return dbPebble
   353  	}
   354  	return dbLeveldb
   355  }
   356  
   357  // OpenOptions contains the options to apply when opening a database.
   358  // OBS: If AncientsDirectory is empty, it indicates that no freezer is to be used.
   359  type OpenOptions struct {
   360  	Type              string // "leveldb" | "pebble"
   361  	Directory         string // the datadir
   362  	AncientsDirectory string // the ancients-dir
   363  	Namespace         string // the namespace for database relevant metrics
   364  	Cache             int    // the capacity(in megabytes) of the data caching
   365  	Handles           int    // number of files to be open simultaneously
   366  	ReadOnly          bool
   367  	// Ephemeral means that filesystem sync operations should be avoided: data integrity in the face of
   368  	// a crash is not important. This option should typically be used in tests.
   369  	Ephemeral bool
   370  }
   371  
   372  // openKeyValueDatabase opens a disk-based key-value database, e.g. leveldb or pebble.
   373  //
   374  //	                      type == null          type != null
   375  //	                   +----------------------------------------
   376  //	db is non-existent |  pebble default  |  specified type
   377  //	db is existent     |  from db         |  specified type (if compatible)
   378  func openKeyValueDatabase(o OpenOptions) (ethdb.Database, error) {
   379  	// Reject any unsupported database type
   380  	if len(o.Type) != 0 && o.Type != dbLeveldb && o.Type != dbPebble {
   381  		return nil, fmt.Errorf("unknown db.engine %v", o.Type)
   382  	}
   383  	// Retrieve any pre-existing database's type and use that or the requested one
   384  	// as long as there's no conflict between the two types
   385  	existingDb := PreexistingDatabase(o.Directory)
   386  	if len(existingDb) != 0 && len(o.Type) != 0 && o.Type != existingDb {
   387  		return nil, fmt.Errorf("db.engine choice was %v but found pre-existing %v database in specified data directory", o.Type, existingDb)
   388  	}
   389  	if o.Type == dbPebble || existingDb == dbPebble {
   390  		log.Info("Using pebble as the backing database")
   391  		return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.Ephemeral)
   392  	}
   393  	if o.Type == dbLeveldb || existingDb == dbLeveldb {
   394  		log.Info("Using leveldb as the backing database")
   395  		return NewLevelDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly)
   396  	}
   397  	// No pre-existing database, no user-requested one either. Default to Pebble.
   398  	log.Info("Defaulting to pebble as the backing database")
   399  	return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.Ephemeral)
   400  }
   401  
   402  // Open opens both a disk-based key-value database such as leveldb or pebble, but also
   403  // integrates it with a freezer database -- if the AncientDir option has been
   404  // set on the provided OpenOptions.
   405  // The passed o.AncientDir indicates the path of root ancient directory where
   406  // the chain freezer can be opened.
   407  func Open(o OpenOptions) (ethdb.Database, error) {
   408  	kvdb, err := openKeyValueDatabase(o)
   409  	if err != nil {
   410  		return nil, err
   411  	}
   412  	if len(o.AncientsDirectory) == 0 {
   413  		return kvdb, nil
   414  	}
   415  	frdb, err := NewDatabaseWithFreezer(kvdb, o.AncientsDirectory, o.Namespace, o.ReadOnly)
   416  	if err != nil {
   417  		kvdb.Close()
   418  		return nil, err
   419  	}
   420  	return frdb, nil
   421  }
   422  
   423  type counter uint64
   424  
   425  func (c counter) String() string {
   426  	return fmt.Sprintf("%d", c)
   427  }
   428  
   429  func (c counter) Percentage(current uint64) string {
   430  	return fmt.Sprintf("%d", current*100/uint64(c))
   431  }
   432  
   433  // stat stores sizes and count for a parameter
   434  type stat struct {
   435  	size  common.StorageSize
   436  	count counter
   437  }
   438  
   439  // Add size to the stat and increase the counter by 1
   440  func (s *stat) Add(size common.StorageSize) {
   441  	s.size += size
   442  	s.count++
   443  }
   444  
   445  func (s *stat) Size() string {
   446  	return s.size.String()
   447  }
   448  
   449  func (s *stat) Count() string {
   450  	return s.count.String()
   451  }
   452  
   453  // InspectDatabase traverses the entire database and checks the size
   454  // of all different categories of data.
   455  func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
   456  	it := db.NewIterator(keyPrefix, keyStart)
   457  	defer it.Release()
   458  
   459  	var (
   460  		count  int64
   461  		start  = time.Now()
   462  		logged = time.Now()
   463  
   464  		// Key-value store statistics
   465  		headers         stat
   466  		bodies          stat
   467  		receipts        stat
   468  		tds             stat
   469  		numHashPairings stat
   470  		hashNumPairings stat
   471  		legacyTries     stat
   472  		stateLookups    stat
   473  		accountTries    stat
   474  		storageTries    stat
   475  		codes           stat
   476  		txLookups       stat
   477  		accountSnaps    stat
   478  		storageSnaps    stat
   479  		preimages       stat
   480  		bloomBits       stat
   481  		beaconHeaders   stat
   482  		cliqueSnaps     stat
   483  
   484  		// Les statistic
   485  		chtTrieNodes   stat
   486  		bloomTrieNodes stat
   487  
   488  		// Meta- and unaccounted data
   489  		metadata    stat
   490  		unaccounted stat
   491  
   492  		// Totals
   493  		total common.StorageSize
   494  	)
   495  	// Inspect key-value database first.
   496  	for it.Next() {
   497  		var (
   498  			key  = it.Key()
   499  			size = common.StorageSize(len(key) + len(it.Value()))
   500  		)
   501  		total += size
   502  		switch {
   503  		case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength):
   504  			headers.Add(size)
   505  		case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength):
   506  			bodies.Add(size)
   507  		case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength):
   508  			receipts.Add(size)
   509  		case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix):
   510  			tds.Add(size)
   511  		case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix):
   512  			numHashPairings.Add(size)
   513  		case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
   514  			hashNumPairings.Add(size)
   515  		case IsLegacyTrieNode(key, it.Value()):
   516  			legacyTries.Add(size)
   517  		case bytes.HasPrefix(key, stateIDPrefix) && len(key) == len(stateIDPrefix)+common.HashLength:
   518  			stateLookups.Add(size)
   519  		case IsAccountTrieNode(key):
   520  			accountTries.Add(size)
   521  		case IsStorageTrieNode(key):
   522  			storageTries.Add(size)
   523  		case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength:
   524  			codes.Add(size)
   525  		case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength):
   526  			txLookups.Add(size)
   527  		case bytes.HasPrefix(key, SnapshotAccountPrefix) && len(key) == (len(SnapshotAccountPrefix)+common.HashLength):
   528  			accountSnaps.Add(size)
   529  		case bytes.HasPrefix(key, SnapshotStoragePrefix) && len(key) == (len(SnapshotStoragePrefix)+2*common.HashLength):
   530  			storageSnaps.Add(size)
   531  		case bytes.HasPrefix(key, PreimagePrefix) && len(key) == (len(PreimagePrefix)+common.HashLength):
   532  			preimages.Add(size)
   533  		case bytes.HasPrefix(key, configPrefix) && len(key) == (len(configPrefix)+common.HashLength):
   534  			metadata.Add(size)
   535  		case bytes.HasPrefix(key, genesisPrefix) && len(key) == (len(genesisPrefix)+common.HashLength):
   536  			metadata.Add(size)
   537  		case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength):
   538  			bloomBits.Add(size)
   539  		case bytes.HasPrefix(key, BloomBitsIndexPrefix):
   540  			bloomBits.Add(size)
   541  		case bytes.HasPrefix(key, skeletonHeaderPrefix) && len(key) == (len(skeletonHeaderPrefix)+8):
   542  			beaconHeaders.Add(size)
   543  		case bytes.HasPrefix(key, CliqueSnapshotPrefix) && len(key) == 7+common.HashLength:
   544  			cliqueSnaps.Add(size)
   545  		case bytes.HasPrefix(key, ChtTablePrefix) ||
   546  			bytes.HasPrefix(key, ChtIndexTablePrefix) ||
   547  			bytes.HasPrefix(key, ChtPrefix): // Canonical hash trie
   548  			chtTrieNodes.Add(size)
   549  		case bytes.HasPrefix(key, BloomTrieTablePrefix) ||
   550  			bytes.HasPrefix(key, BloomTrieIndexPrefix) ||
   551  			bytes.HasPrefix(key, BloomTriePrefix): // Bloomtrie sub
   552  			bloomTrieNodes.Add(size)
   553  		default:
   554  			var accounted bool
   555  			for _, meta := range [][]byte{
   556  				databaseVersionKey, headHeaderKey, headBlockKey, headFastBlockKey, headFinalizedBlockKey,
   557  				lastPivotKey, fastTrieProgressKey, snapshotDisabledKey, SnapshotRootKey, snapshotJournalKey,
   558  				snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey,
   559  				uncleanShutdownKey, badBlockKey, transitionStatusKey, skeletonSyncStatusKey,
   560  				persistentStateIDKey, trieJournalKey, snapshotSyncStatusKey, snapSyncStatusFlagKey,
   561  			} {
   562  				if bytes.Equal(key, meta) {
   563  					metadata.Add(size)
   564  					accounted = true
   565  					break
   566  				}
   567  			}
   568  			if !accounted {
   569  				unaccounted.Add(size)
   570  			}
   571  		}
   572  		count++
   573  		if count%1000 == 0 && time.Since(logged) > 8*time.Second {
   574  			log.Info("Inspecting database", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
   575  			logged = time.Now()
   576  		}
   577  	}
   578  	// Display the database statistic of key-value store.
   579  	stats := [][]string{
   580  		{"Key-Value store", "Headers", headers.Size(), headers.Count()},
   581  		{"Key-Value store", "Bodies", bodies.Size(), bodies.Count()},
   582  		{"Key-Value store", "Receipt lists", receipts.Size(), receipts.Count()},
   583  		{"Key-Value store", "Difficulties", tds.Size(), tds.Count()},
   584  		{"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()},
   585  		{"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()},
   586  		{"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()},
   587  		{"Key-Value store", "Bloombit index", bloomBits.Size(), bloomBits.Count()},
   588  		{"Key-Value store", "Contract codes", codes.Size(), codes.Count()},
   589  		{"Key-Value store", "Hash trie nodes", legacyTries.Size(), legacyTries.Count()},
   590  		{"Key-Value store", "Path trie state lookups", stateLookups.Size(), stateLookups.Count()},
   591  		{"Key-Value store", "Path trie account nodes", accountTries.Size(), accountTries.Count()},
   592  		{"Key-Value store", "Path trie storage nodes", storageTries.Size(), storageTries.Count()},
   593  		{"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()},
   594  		{"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()},
   595  		{"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()},
   596  		{"Key-Value store", "Beacon sync headers", beaconHeaders.Size(), beaconHeaders.Count()},
   597  		{"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()},
   598  		{"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()},
   599  		{"Light client", "CHT trie nodes", chtTrieNodes.Size(), chtTrieNodes.Count()},
   600  		{"Light client", "Bloom trie nodes", bloomTrieNodes.Size(), bloomTrieNodes.Count()},
   601  	}
   602  	// Inspect all registered append-only file store then.
   603  	ancients, err := inspectFreezers(db)
   604  	if err != nil {
   605  		return err
   606  	}
   607  	for _, ancient := range ancients {
   608  		for _, table := range ancient.sizes {
   609  			stats = append(stats, []string{
   610  				fmt.Sprintf("Ancient store (%s)", strings.Title(ancient.name)),
   611  				strings.Title(table.name),
   612  				table.size.String(),
   613  				fmt.Sprintf("%d", ancient.count()),
   614  			})
   615  		}
   616  		total += ancient.size()
   617  	}
   618  	table := tablewriter.NewWriter(os.Stdout)
   619  	table.SetHeader([]string{"Database", "Category", "Size", "Items"})
   620  	table.SetFooter([]string{"", "Total", total.String(), " "})
   621  	table.AppendBulk(stats)
   622  	table.Render()
   623  
   624  	if unaccounted.size > 0 {
   625  		log.Error("Database contains unaccounted data", "size", unaccounted.size, "count", unaccounted.count)
   626  	}
   627  	return nil
   628  }
   629  
   630  // printChainMetadata prints out chain metadata to stderr.
   631  func printChainMetadata(db ethdb.KeyValueStore) {
   632  	fmt.Fprintf(os.Stderr, "Chain metadata\n")
   633  	for _, v := range ReadChainMetadata(db) {
   634  		fmt.Fprintf(os.Stderr, "  %s\n", strings.Join(v, ": "))
   635  	}
   636  	fmt.Fprintf(os.Stderr, "\n\n")
   637  }
   638  
   639  // ReadChainMetadata returns a set of key/value pairs that contains information
   640  // about the database chain status. This can be used for diagnostic purposes
   641  // when investigating the state of the node.
   642  func ReadChainMetadata(db ethdb.KeyValueStore) [][]string {
   643  	pp := func(val *uint64) string {
   644  		if val == nil {
   645  			return "<nil>"
   646  		}
   647  		return fmt.Sprintf("%d (%#x)", *val, *val)
   648  	}
   649  	data := [][]string{
   650  		{"databaseVersion", pp(ReadDatabaseVersion(db))},
   651  		{"headBlockHash", fmt.Sprintf("%v", ReadHeadBlockHash(db))},
   652  		{"headFastBlockHash", fmt.Sprintf("%v", ReadHeadFastBlockHash(db))},
   653  		{"headHeaderHash", fmt.Sprintf("%v", ReadHeadHeaderHash(db))},
   654  		{"lastPivotNumber", pp(ReadLastPivotNumber(db))},
   655  		{"len(snapshotSyncStatus)", fmt.Sprintf("%d bytes", len(ReadSnapshotSyncStatus(db)))},
   656  		{"snapshotDisabled", fmt.Sprintf("%v", ReadSnapshotDisabled(db))},
   657  		{"snapshotJournal", fmt.Sprintf("%d bytes", len(ReadSnapshotJournal(db)))},
   658  		{"snapshotRecoveryNumber", pp(ReadSnapshotRecoveryNumber(db))},
   659  		{"snapshotRoot", fmt.Sprintf("%v", ReadSnapshotRoot(db))},
   660  		{"txIndexTail", pp(ReadTxIndexTail(db))},
   661  	}
   662  	if b := ReadSkeletonSyncStatus(db); b != nil {
   663  		data = append(data, []string{"SkeletonSyncStatus", string(b)})
   664  	}
   665  	return data
   666  }