gitlab.com/flarenetwork/coreth@v0.1.1/core/rawdb/database.go (about)

     1  // (c) 2019-2020, Ava Labs, Inc.
     2  //
     3  // This file is a derived work, based on the go-ethereum library whose original
     4  // notices appear below.
     5  //
     6  // It is distributed under a license compatible with the licensing terms of the
     7  // original code from which it is derived.
     8  //
     9  // Much love to the original authors for their work.
    10  // **********
    11  // Copyright 2018 The go-ethereum Authors
    12  // This file is part of the go-ethereum library.
    13  //
    14  // The go-ethereum library is free software: you can redistribute it and/or modify
    15  // it under the terms of the GNU Lesser General Public License as published by
    16  // the Free Software Foundation, either version 3 of the License, or
    17  // (at your option) any later version.
    18  //
    19  // The go-ethereum library is distributed in the hope that it will be useful,
    20  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    21  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    22  // GNU Lesser General Public License for more details.
    23  //
    24  // You should have received a copy of the GNU Lesser General Public License
    25  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    26  
    27  package rawdb
    28  
    29  import (
    30  	"bytes"
    31  	"errors"
    32  	"fmt"
    33  	"os"
    34  	"time"
    35  
    36  	"github.com/ethereum/go-ethereum/common"
    37  	"github.com/ethereum/go-ethereum/ethdb"
    38  	"github.com/ethereum/go-ethereum/ethdb/leveldb"
    39  	"github.com/ethereum/go-ethereum/ethdb/memorydb"
    40  	"github.com/ethereum/go-ethereum/log"
    41  	"github.com/olekukonko/tablewriter"
    42  )
    43  
    44  var (
    45  	// errNotSupported is returned if the database doesn't support the required operation.
    46  	errNotSupported = errors.New("this operation is not supported")
    47  )
    48  
    49  // nofreezedb is a database wrapper that disables freezer data retrievals.
    50  type nofreezedb struct {
    51  	ethdb.KeyValueStore
    52  }
    53  
    54  // HasAncient returns an error as we don't have a backing chain freezer.
    55  func (db *nofreezedb) HasAncient(kind string, number uint64) (bool, error) {
    56  	return false, errNotSupported
    57  }
    58  
    59  // Ancient returns an error as we don't have a backing chain freezer.
    60  func (db *nofreezedb) Ancient(kind string, number uint64) ([]byte, error) {
    61  	return nil, errNotSupported
    62  }
    63  
    64  // Ancients returns an error as we don't have a backing chain freezer.
    65  func (db *nofreezedb) Ancients() (uint64, error) {
    66  	return 0, errNotSupported
    67  }
    68  
    69  // AncientSize returns an error as we don't have a backing chain freezer.
    70  func (db *nofreezedb) AncientSize(kind string) (uint64, error) {
    71  	return 0, errNotSupported
    72  }
    73  
    74  // AppendAncient returns an error as we don't have a backing chain freezer.
    75  func (db *nofreezedb) AppendAncient(number uint64, hash, header, body, receipts, td []byte) error {
    76  	return errNotSupported
    77  }
    78  
    79  // TruncateAncients returns an error as we don't have a backing chain freezer.
    80  func (db *nofreezedb) TruncateAncients(items uint64) error {
    81  	return errNotSupported
    82  }
    83  
    84  // Sync returns an error as we don't have a backing chain freezer.
    85  func (db *nofreezedb) Sync() error {
    86  	return errNotSupported
    87  }
    88  
    89  // NewDatabase creates a high level database on top of a given key-value data
    90  // store without a freezer moving immutable chain segments into cold storage.
    91  func NewDatabase(db ethdb.KeyValueStore) ethdb.Database {
    92  	return &nofreezedb{
    93  		KeyValueStore: db,
    94  	}
    95  }
    96  
    97  // NewMemoryDatabase creates an ephemeral in-memory key-value database without a
    98  // freezer moving immutable chain segments into cold storage.
    99  func NewMemoryDatabase() ethdb.Database {
   100  	return NewDatabase(memorydb.New())
   101  }
   102  
   103  // NewMemoryDatabaseWithCap creates an ephemeral in-memory key-value database
   104  // with an initial starting capacity, but without a freezer moving immutable
   105  // chain segments into cold storage.
   106  func NewMemoryDatabaseWithCap(size int) ethdb.Database {
   107  	return NewDatabase(memorydb.NewWithCap(size))
   108  }
   109  
   110  // NewLevelDBDatabase creates a persistent key-value database without a freezer
   111  // moving immutable chain segments into cold storage.
   112  func NewLevelDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) {
   113  	db, err := leveldb.New(file, cache, handles, namespace, readonly)
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  	return NewDatabase(db), nil
   118  }
   119  
   120  type counter uint64
   121  
   122  func (c counter) String() string {
   123  	return fmt.Sprintf("%d", c)
   124  }
   125  
   126  func (c counter) Percentage(current uint64) string {
   127  	return fmt.Sprintf("%d", current*100/uint64(c))
   128  }
   129  
   130  // stat stores sizes and count for a parameter
   131  type stat struct {
   132  	size  common.StorageSize
   133  	count counter
   134  }
   135  
   136  // Add size to the stat and increase the counter by 1
   137  func (s *stat) Add(size common.StorageSize) {
   138  	s.size += size
   139  	s.count++
   140  }
   141  
   142  func (s *stat) Size() string {
   143  	return s.size.String()
   144  }
   145  
   146  func (s *stat) Count() string {
   147  	return s.count.String()
   148  }
   149  
   150  // InspectDatabase traverses the entire database and checks the size
   151  // of all different categories of data.
   152  func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
   153  	it := db.NewIterator(keyPrefix, keyStart)
   154  	defer it.Release()
   155  
   156  	var (
   157  		count  int64
   158  		start  = time.Now()
   159  		logged = time.Now()
   160  
   161  		// Key-value store statistics
   162  		headers         stat
   163  		bodies          stat
   164  		receipts        stat
   165  		tds             stat
   166  		numHashPairings stat
   167  		hashNumPairings stat
   168  		tries           stat
   169  		codes           stat
   170  		txLookups       stat
   171  		accountSnaps    stat
   172  		storageSnaps    stat
   173  		preimages       stat
   174  		bloomBits       stat
   175  		cliqueSnaps     stat
   176  
   177  		// Ancient store statistics
   178  		ancientHeadersSize  common.StorageSize
   179  		ancientBodiesSize   common.StorageSize
   180  		ancientReceiptsSize common.StorageSize
   181  		ancientTdsSize      common.StorageSize
   182  		ancientHashesSize   common.StorageSize
   183  
   184  		// Les statistic
   185  		chtTrieNodes   stat
   186  		bloomTrieNodes stat
   187  
   188  		// Meta- and unaccounted data
   189  		metadata    stat
   190  		unaccounted stat
   191  
   192  		// Totals
   193  		total common.StorageSize
   194  	)
   195  	// Inspect key-value database first.
   196  	for it.Next() {
   197  		var (
   198  			key  = it.Key()
   199  			size = common.StorageSize(len(key) + len(it.Value()))
   200  		)
   201  		total += size
   202  		switch {
   203  		case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength):
   204  			headers.Add(size)
   205  		case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength):
   206  			bodies.Add(size)
   207  		case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength):
   208  			receipts.Add(size)
   209  		case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix):
   210  			tds.Add(size)
   211  		case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix):
   212  			numHashPairings.Add(size)
   213  		case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
   214  			hashNumPairings.Add(size)
   215  		case len(key) == common.HashLength:
   216  			tries.Add(size)
   217  		case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength:
   218  			codes.Add(size)
   219  		case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength):
   220  			txLookups.Add(size)
   221  		case bytes.HasPrefix(key, SnapshotAccountPrefix) && len(key) == (len(SnapshotAccountPrefix)+common.HashLength):
   222  			accountSnaps.Add(size)
   223  		case bytes.HasPrefix(key, SnapshotStoragePrefix) && len(key) == (len(SnapshotStoragePrefix)+2*common.HashLength):
   224  			storageSnaps.Add(size)
   225  		case bytes.HasPrefix(key, preimagePrefix) && len(key) == (len(preimagePrefix)+common.HashLength):
   226  			preimages.Add(size)
   227  		case bytes.HasPrefix(key, configPrefix) && len(key) == (len(configPrefix)+common.HashLength):
   228  			metadata.Add(size)
   229  		case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength):
   230  			bloomBits.Add(size)
   231  		case bytes.HasPrefix(key, BloomBitsIndexPrefix):
   232  			bloomBits.Add(size)
   233  		case bytes.HasPrefix(key, []byte("clique-")) && len(key) == 7+common.HashLength:
   234  			cliqueSnaps.Add(size)
   235  		case bytes.HasPrefix(key, []byte("cht-")) ||
   236  			bytes.HasPrefix(key, []byte("chtIndexV2-")) ||
   237  			bytes.HasPrefix(key, []byte("chtRootV2-")): // Canonical hash trie
   238  			chtTrieNodes.Add(size)
   239  		case bytes.HasPrefix(key, []byte("blt-")) ||
   240  			bytes.HasPrefix(key, []byte("bltIndex-")) ||
   241  			bytes.HasPrefix(key, []byte("bltRoot-")): // Bloomtrie sub
   242  			bloomTrieNodes.Add(size)
   243  		default:
   244  			var accounted bool
   245  			for _, meta := range [][]byte{
   246  				databaseVersionKey, headHeaderKey, headBlockKey, headFastBlockKey, lastPivotKey,
   247  				fastTrieProgressKey, snapshotDisabledKey, snapshotRootKey, snapshotJournalKey,
   248  				snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey,
   249  				uncleanShutdownKey, badBlockKey,
   250  			} {
   251  				if bytes.Equal(key, meta) {
   252  					metadata.Add(size)
   253  					accounted = true
   254  					break
   255  				}
   256  			}
   257  			if !accounted {
   258  				unaccounted.Add(size)
   259  			}
   260  		}
   261  		count++
   262  		if count%1000 == 0 && time.Since(logged) > 8*time.Second {
   263  			log.Info("Inspecting database", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
   264  			logged = time.Now()
   265  		}
   266  	}
   267  	// Inspect append-only file store then.
   268  	ancientSizes := []*common.StorageSize{&ancientHeadersSize, &ancientBodiesSize, &ancientReceiptsSize, &ancientHashesSize, &ancientTdsSize}
   269  	for i, category := range []string{freezerHeaderTable, freezerBodiesTable, freezerReceiptTable, freezerHashTable, freezerDifficultyTable} {
   270  		if size, err := db.AncientSize(category); err == nil {
   271  			*ancientSizes[i] += common.StorageSize(size)
   272  			total += common.StorageSize(size)
   273  		}
   274  	}
   275  	// Get number of ancient rows inside the freezer
   276  	ancients := counter(0)
   277  	if count, err := db.Ancients(); err == nil {
   278  		ancients = counter(count)
   279  	}
   280  	// Display the database statistic.
   281  	stats := [][]string{
   282  		{"Key-Value store", "Headers", headers.Size(), headers.Count()},
   283  		{"Key-Value store", "Bodies", bodies.Size(), bodies.Count()},
   284  		{"Key-Value store", "Receipt lists", receipts.Size(), receipts.Count()},
   285  		{"Key-Value store", "Difficulties", tds.Size(), tds.Count()},
   286  		{"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()},
   287  		{"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()},
   288  		{"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()},
   289  		{"Key-Value store", "Bloombit index", bloomBits.Size(), bloomBits.Count()},
   290  		{"Key-Value store", "Contract codes", codes.Size(), codes.Count()},
   291  		{"Key-Value store", "Trie nodes", tries.Size(), tries.Count()},
   292  		{"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()},
   293  		{"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()},
   294  		{"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()},
   295  		{"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()},
   296  		{"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()},
   297  		{"Ancient store", "Headers", ancientHeadersSize.String(), ancients.String()},
   298  		{"Ancient store", "Bodies", ancientBodiesSize.String(), ancients.String()},
   299  		{"Ancient store", "Receipt lists", ancientReceiptsSize.String(), ancients.String()},
   300  		{"Ancient store", "Difficulties", ancientTdsSize.String(), ancients.String()},
   301  		{"Ancient store", "Block number->hash", ancientHashesSize.String(), ancients.String()},
   302  		{"Light client", "CHT trie nodes", chtTrieNodes.Size(), chtTrieNodes.Count()},
   303  		{"Light client", "Bloom trie nodes", bloomTrieNodes.Size(), bloomTrieNodes.Count()},
   304  	}
   305  	table := tablewriter.NewWriter(os.Stdout)
   306  	table.SetHeader([]string{"Database", "Category", "Size", "Items"})
   307  	table.SetFooter([]string{"", "Total", total.String(), " "})
   308  	table.AppendBulk(stats)
   309  	table.Render()
   310  
   311  	if unaccounted.size > 0 {
   312  		log.Error("Database contains unaccounted data", "size", unaccounted.size, "count", unaccounted.count)
   313  	}
   314  
   315  	return nil
   316  }