github.com/theQRL/go-zond@v0.2.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  		numHashPairings stat
   467  		hashNumPairings stat
   468  		legacyTries     stat
   469  		stateLookups    stat
   470  		accountTries    stat
   471  		storageTries    stat
   472  		codes           stat
   473  		txLookups       stat
   474  		accountSnaps    stat
   475  		storageSnaps    stat
   476  		preimages       stat
   477  		bloomBits       stat
   478  		beaconHeaders   stat
   479  
   480  		// Les statistic
   481  		chtTrieNodes   stat
   482  		bloomTrieNodes stat
   483  
   484  		// Meta- and unaccounted data
   485  		metadata    stat
   486  		unaccounted stat
   487  
   488  		// Totals
   489  		total common.StorageSize
   490  	)
   491  	// Inspect key-value database first.
   492  	for it.Next() {
   493  		var (
   494  			key  = it.Key()
   495  			size = common.StorageSize(len(key) + len(it.Value()))
   496  		)
   497  		total += size
   498  		switch {
   499  		case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength):
   500  			headers.Add(size)
   501  		case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength):
   502  			bodies.Add(size)
   503  		case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength):
   504  			receipts.Add(size)
   505  		case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix):
   506  			numHashPairings.Add(size)
   507  		case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
   508  			hashNumPairings.Add(size)
   509  		case IsLegacyTrieNode(key, it.Value()):
   510  			legacyTries.Add(size)
   511  		case bytes.HasPrefix(key, stateIDPrefix) && len(key) == len(stateIDPrefix)+common.HashLength:
   512  			stateLookups.Add(size)
   513  		case IsAccountTrieNode(key):
   514  			accountTries.Add(size)
   515  		case IsStorageTrieNode(key):
   516  			storageTries.Add(size)
   517  		case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength:
   518  			codes.Add(size)
   519  		case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength):
   520  			txLookups.Add(size)
   521  		case bytes.HasPrefix(key, SnapshotAccountPrefix) && len(key) == (len(SnapshotAccountPrefix)+common.HashLength):
   522  			accountSnaps.Add(size)
   523  		case bytes.HasPrefix(key, SnapshotStoragePrefix) && len(key) == (len(SnapshotStoragePrefix)+2*common.HashLength):
   524  			storageSnaps.Add(size)
   525  		case bytes.HasPrefix(key, PreimagePrefix) && len(key) == (len(PreimagePrefix)+common.HashLength):
   526  			preimages.Add(size)
   527  		case bytes.HasPrefix(key, configPrefix) && len(key) == (len(configPrefix)+common.HashLength):
   528  			metadata.Add(size)
   529  		case bytes.HasPrefix(key, genesisPrefix) && len(key) == (len(genesisPrefix)+common.HashLength):
   530  			metadata.Add(size)
   531  		case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength):
   532  			bloomBits.Add(size)
   533  		case bytes.HasPrefix(key, BloomBitsIndexPrefix):
   534  			bloomBits.Add(size)
   535  		case bytes.HasPrefix(key, skeletonHeaderPrefix) && len(key) == (len(skeletonHeaderPrefix)+8):
   536  			beaconHeaders.Add(size)
   537  		case bytes.HasPrefix(key, ChtTablePrefix) ||
   538  			bytes.HasPrefix(key, ChtIndexTablePrefix) ||
   539  			bytes.HasPrefix(key, ChtPrefix): // Canonical hash trie
   540  			chtTrieNodes.Add(size)
   541  		case bytes.HasPrefix(key, BloomTrieTablePrefix) ||
   542  			bytes.HasPrefix(key, BloomTrieIndexPrefix) ||
   543  			bytes.HasPrefix(key, BloomTriePrefix): // Bloomtrie sub
   544  			bloomTrieNodes.Add(size)
   545  		default:
   546  			var accounted bool
   547  			for _, meta := range [][]byte{
   548  				databaseVersionKey, headHeaderKey, headBlockKey, headFastBlockKey, headFinalizedBlockKey,
   549  				lastPivotKey, fastTrieProgressKey, snapshotDisabledKey, SnapshotRootKey, snapshotJournalKey,
   550  				snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey,
   551  				uncleanShutdownKey, badBlockKey, transitionStatusKey, skeletonSyncStatusKey,
   552  				persistentStateIDKey, trieJournalKey, snapshotSyncStatusKey,
   553  			} {
   554  				if bytes.Equal(key, meta) {
   555  					metadata.Add(size)
   556  					accounted = true
   557  					break
   558  				}
   559  			}
   560  			if !accounted {
   561  				unaccounted.Add(size)
   562  			}
   563  		}
   564  		count++
   565  		if count%1000 == 0 && time.Since(logged) > 8*time.Second {
   566  			log.Info("Inspecting database", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
   567  			logged = time.Now()
   568  		}
   569  	}
   570  	// Display the database statistic of key-value store.
   571  	stats := [][]string{
   572  		{"Key-Value store", "Headers", headers.Size(), headers.Count()},
   573  		{"Key-Value store", "Bodies", bodies.Size(), bodies.Count()},
   574  		{"Key-Value store", "Receipt lists", receipts.Size(), receipts.Count()},
   575  		{"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()},
   576  		{"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()},
   577  		{"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()},
   578  		{"Key-Value store", "Bloombit index", bloomBits.Size(), bloomBits.Count()},
   579  		{"Key-Value store", "Contract codes", codes.Size(), codes.Count()},
   580  		{"Key-Value store", "Hash trie nodes", legacyTries.Size(), legacyTries.Count()},
   581  		{"Key-Value store", "Path trie state lookups", stateLookups.Size(), stateLookups.Count()},
   582  		{"Key-Value store", "Path trie account nodes", accountTries.Size(), accountTries.Count()},
   583  		{"Key-Value store", "Path trie storage nodes", storageTries.Size(), storageTries.Count()},
   584  		{"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()},
   585  		{"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()},
   586  		{"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()},
   587  		{"Key-Value store", "Beacon sync headers", beaconHeaders.Size(), beaconHeaders.Count()},
   588  		{"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()},
   589  		{"Light client", "CHT trie nodes", chtTrieNodes.Size(), chtTrieNodes.Count()},
   590  		{"Light client", "Bloom trie nodes", bloomTrieNodes.Size(), bloomTrieNodes.Count()},
   591  	}
   592  	// Inspect all registered append-only file store then.
   593  	ancients, err := inspectFreezers(db)
   594  	if err != nil {
   595  		return err
   596  	}
   597  	for _, ancient := range ancients {
   598  		for _, table := range ancient.sizes {
   599  			stats = append(stats, []string{
   600  				fmt.Sprintf("Ancient store (%s)", strings.Title(ancient.name)),
   601  				strings.Title(table.name),
   602  				table.size.String(),
   603  				fmt.Sprintf("%d", ancient.count()),
   604  			})
   605  		}
   606  		total += ancient.size()
   607  	}
   608  	table := tablewriter.NewWriter(os.Stdout)
   609  	table.SetHeader([]string{"Database", "Category", "Size", "Items"})
   610  	table.SetFooter([]string{"", "Total", total.String(), " "})
   611  	table.AppendBulk(stats)
   612  	table.Render()
   613  
   614  	if unaccounted.size > 0 {
   615  		log.Error("Database contains unaccounted data", "size", unaccounted.size, "count", unaccounted.count)
   616  	}
   617  	return nil
   618  }
   619  
   620  // printChainMetadata prints out chain metadata to stderr.
   621  func printChainMetadata(db zonddb.KeyValueStore) {
   622  	fmt.Fprintf(os.Stderr, "Chain metadata\n")
   623  	for _, v := range ReadChainMetadata(db) {
   624  		fmt.Fprintf(os.Stderr, "  %s\n", strings.Join(v, ": "))
   625  	}
   626  	fmt.Fprintf(os.Stderr, "\n\n")
   627  }
   628  
   629  // ReadChainMetadata returns a set of key/value pairs that contains informatin
   630  // about the database chain status. This can be used for diagnostic purposes
   631  // when investigating the state of the node.
   632  func ReadChainMetadata(db zonddb.KeyValueStore) [][]string {
   633  	pp := func(val *uint64) string {
   634  		if val == nil {
   635  			return "<nil>"
   636  		}
   637  		return fmt.Sprintf("%d (%#x)", *val, *val)
   638  	}
   639  	data := [][]string{
   640  		{"databaseVersion", pp(ReadDatabaseVersion(db))},
   641  		{"headBlockHash", fmt.Sprintf("%v", ReadHeadBlockHash(db))},
   642  		{"headFastBlockHash", fmt.Sprintf("%v", ReadHeadFastBlockHash(db))},
   643  		{"headHeaderHash", fmt.Sprintf("%v", ReadHeadHeaderHash(db))},
   644  		{"lastPivotNumber", pp(ReadLastPivotNumber(db))},
   645  		{"len(snapshotSyncStatus)", fmt.Sprintf("%d bytes", len(ReadSnapshotSyncStatus(db)))},
   646  		{"snapshotDisabled", fmt.Sprintf("%v", ReadSnapshotDisabled(db))},
   647  		{"snapshotJournal", fmt.Sprintf("%d bytes", len(ReadSnapshotJournal(db)))},
   648  		{"snapshotRecoveryNumber", pp(ReadSnapshotRecoveryNumber(db))},
   649  		{"snapshotRoot", fmt.Sprintf("%v", ReadSnapshotRoot(db))},
   650  		{"txIndexTail", pp(ReadTxIndexTail(db))},
   651  		{"fastTxLookupLimit", pp(ReadFastTxLookupLimit(db))},
   652  	}
   653  	if b := ReadSkeletonSyncStatus(db); b != nil {
   654  		data = append(data, []string{"SkeletonSyncStatus", string(b)})
   655  	}
   656  	return data
   657  }