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