github.com/dim4egster/coreth@v0.10.2/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  	"fmt"
    32  	"os"
    33  	"time"
    34  
    35  	"github.com/dim4egster/coreth/ethdb"
    36  	"github.com/dim4egster/coreth/ethdb/leveldb"
    37  	"github.com/dim4egster/coreth/ethdb/memorydb"
    38  	"github.com/ethereum/go-ethereum/common"
    39  	"github.com/ethereum/go-ethereum/log"
    40  	"github.com/olekukonko/tablewriter"
    41  )
    42  
    43  // nofreezedb is a database wrapper that disables freezer data retrievals.
    44  type nofreezedb struct {
    45  	ethdb.KeyValueStore
    46  }
    47  
    48  // NewDatabase creates a high level database on top of a given key-value data
    49  // store without a freezer moving immutable chain segments into cold storage.
    50  func NewDatabase(db ethdb.KeyValueStore) ethdb.Database {
    51  	return &nofreezedb{KeyValueStore: db}
    52  }
    53  
    54  // NewMemoryDatabase creates an ephemeral in-memory key-value database without a
    55  // freezer moving immutable chain segments into cold storage.
    56  func NewMemoryDatabase() ethdb.Database {
    57  	return NewDatabase(memorydb.New())
    58  }
    59  
    60  // NewMemoryDatabaseWithCap creates an ephemeral in-memory key-value database
    61  // with an initial starting capacity, but without a freezer moving immutable
    62  // chain segments into cold storage.
    63  func NewMemoryDatabaseWithCap(size int) ethdb.Database {
    64  	return NewDatabase(memorydb.NewWithCap(size))
    65  }
    66  
    67  // NewLevelDBDatabase creates a persistent key-value database without a freezer
    68  // moving immutable chain segments into cold storage.
    69  func NewLevelDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) {
    70  	db, err := leveldb.New(file, cache, handles, namespace, readonly)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	return NewDatabase(db), nil
    75  }
    76  
    77  type counter uint64
    78  
    79  func (c counter) String() string {
    80  	return fmt.Sprintf("%d", c)
    81  }
    82  
    83  func (c counter) Percentage(current uint64) string {
    84  	return fmt.Sprintf("%d", current*100/uint64(c))
    85  }
    86  
    87  // stat stores sizes and count for a parameter
    88  type stat struct {
    89  	size  common.StorageSize
    90  	count counter
    91  }
    92  
    93  // Add size to the stat and increase the counter by 1
    94  func (s *stat) Add(size common.StorageSize) {
    95  	s.size += size
    96  	s.count++
    97  }
    98  
    99  func (s *stat) Size() string {
   100  	return s.size.String()
   101  }
   102  
   103  func (s *stat) Count() string {
   104  	return s.count.String()
   105  }
   106  
   107  // InspectDatabase traverses the entire database and checks the size
   108  // of all different categories of data.
   109  func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
   110  	it := db.NewIterator(keyPrefix, keyStart)
   111  	defer it.Release()
   112  
   113  	var (
   114  		count  int64
   115  		start  = time.Now()
   116  		logged = time.Now()
   117  
   118  		// Key-value store statistics
   119  		headers         stat
   120  		bodies          stat
   121  		receipts        stat
   122  		numHashPairings stat
   123  		hashNumPairings stat
   124  		tries           stat
   125  		codes           stat
   126  		txLookups       stat
   127  		accountSnaps    stat
   128  		storageSnaps    stat
   129  		preimages       stat
   130  		bloomBits       stat
   131  		cliqueSnaps     stat
   132  
   133  		// State sync statistics
   134  		codeToFetch  stat
   135  		syncProgress stat
   136  		syncSegments stat
   137  
   138  		// Les statistic
   139  		chtTrieNodes   stat
   140  		bloomTrieNodes stat
   141  
   142  		// Meta- and unaccounted data
   143  		metadata    stat
   144  		unaccounted stat
   145  
   146  		// Totals
   147  		total common.StorageSize
   148  	)
   149  	// Inspect key-value database first.
   150  	for it.Next() {
   151  		var (
   152  			key  = it.Key()
   153  			size = common.StorageSize(len(key) + len(it.Value()))
   154  		)
   155  		total += size
   156  		switch {
   157  		case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength):
   158  			headers.Add(size)
   159  		case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength):
   160  			bodies.Add(size)
   161  		case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength):
   162  			receipts.Add(size)
   163  		case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix):
   164  			numHashPairings.Add(size)
   165  		case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
   166  			hashNumPairings.Add(size)
   167  		case len(key) == common.HashLength:
   168  			tries.Add(size)
   169  		case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength:
   170  			codes.Add(size)
   171  		case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength):
   172  			txLookups.Add(size)
   173  		case bytes.HasPrefix(key, SnapshotAccountPrefix) && len(key) == (len(SnapshotAccountPrefix)+common.HashLength):
   174  			accountSnaps.Add(size)
   175  		case bytes.HasPrefix(key, SnapshotStoragePrefix) && len(key) == (len(SnapshotStoragePrefix)+2*common.HashLength):
   176  			storageSnaps.Add(size)
   177  		case bytes.HasPrefix(key, preimagePrefix) && len(key) == (len(preimagePrefix)+common.HashLength):
   178  			preimages.Add(size)
   179  		case bytes.HasPrefix(key, configPrefix) && len(key) == (len(configPrefix)+common.HashLength):
   180  			metadata.Add(size)
   181  		case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength):
   182  			bloomBits.Add(size)
   183  		case bytes.HasPrefix(key, BloomBitsIndexPrefix):
   184  			bloomBits.Add(size)
   185  		case bytes.HasPrefix(key, []byte("clique-")) && len(key) == 7+common.HashLength:
   186  			cliqueSnaps.Add(size)
   187  		case bytes.HasPrefix(key, []byte("cht-")) ||
   188  			bytes.HasPrefix(key, []byte("chtIndexV2-")) ||
   189  			bytes.HasPrefix(key, []byte("chtRootV2-")): // Canonical hash trie
   190  			chtTrieNodes.Add(size)
   191  		case bytes.HasPrefix(key, []byte("blt-")) ||
   192  			bytes.HasPrefix(key, []byte("bltIndex-")) ||
   193  			bytes.HasPrefix(key, []byte("bltRoot-")): // Bloomtrie sub
   194  			bloomTrieNodes.Add(size)
   195  		case bytes.HasPrefix(key, syncStorageTriesPrefix) && len(key) == syncStorageTriesKeyLength:
   196  			syncProgress.Add(size)
   197  		case bytes.HasPrefix(key, syncSegmentsPrefix) && len(key) == syncSegmentsKeyLength:
   198  			syncSegments.Add(size)
   199  		case bytes.HasPrefix(key, CodeToFetchPrefix) && len(key) == codeToFetchKeyLength:
   200  			codeToFetch.Add(size)
   201  		default:
   202  			var accounted bool
   203  			for _, meta := range [][]byte{
   204  				databaseVersionKey, headHeaderKey, headBlockKey,
   205  				snapshotRootKey, snapshotBlockHashKey, snapshotGeneratorKey,
   206  				uncleanShutdownKey, syncRootKey,
   207  			} {
   208  				if bytes.Equal(key, meta) {
   209  					metadata.Add(size)
   210  					accounted = true
   211  					break
   212  				}
   213  			}
   214  			if !accounted {
   215  				unaccounted.Add(size)
   216  			}
   217  		}
   218  		count++
   219  		if count%1000 == 0 && time.Since(logged) > 8*time.Second {
   220  			log.Info("Inspecting database", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
   221  			logged = time.Now()
   222  		}
   223  	}
   224  	// Display the database statistic.
   225  	stats := [][]string{
   226  		{"Key-Value store", "Headers", headers.Size(), headers.Count()},
   227  		{"Key-Value store", "Bodies", bodies.Size(), bodies.Count()},
   228  		{"Key-Value store", "Receipt lists", receipts.Size(), receipts.Count()},
   229  		{"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()},
   230  		{"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()},
   231  		{"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()},
   232  		{"Key-Value store", "Bloombit index", bloomBits.Size(), bloomBits.Count()},
   233  		{"Key-Value store", "Contract codes", codes.Size(), codes.Count()},
   234  		{"Key-Value store", "Trie nodes", tries.Size(), tries.Count()},
   235  		{"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()},
   236  		{"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()},
   237  		{"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()},
   238  		{"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()},
   239  		{"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()},
   240  		{"Light client", "CHT trie nodes", chtTrieNodes.Size(), chtTrieNodes.Count()},
   241  		{"Light client", "Bloom trie nodes", bloomTrieNodes.Size(), bloomTrieNodes.Count()},
   242  		{"State sync", "Trie segments", syncSegments.Size(), syncSegments.Count()},
   243  		{"State sync", "Storage tries to fetch", syncProgress.Size(), syncProgress.Count()},
   244  		{"State sync", "Code to fetch", codeToFetch.Size(), codeToFetch.Count()},
   245  	}
   246  	table := tablewriter.NewWriter(os.Stdout)
   247  	table.SetHeader([]string{"Database", "Category", "Size", "Items"})
   248  	table.SetFooter([]string{"", "Total", total.String(), " "})
   249  	table.AppendBulk(stats)
   250  	table.Render()
   251  
   252  	if unaccounted.size > 0 {
   253  		log.Error("Database contains unaccounted data", "size", unaccounted.size, "count", unaccounted.count)
   254  	}
   255  
   256  	return nil
   257  }
   258  
   259  // ClearPrefix removes all keys in db that begin with prefix
   260  func ClearPrefix(db ethdb.KeyValueStore, prefix []byte) error {
   261  	it := db.NewIterator(prefix, nil)
   262  	defer it.Release()
   263  
   264  	batch := db.NewBatch()
   265  	for it.Next() {
   266  		key := common.CopyBytes(it.Key())
   267  		if err := batch.Delete(key); err != nil {
   268  			return err
   269  		}
   270  		if batch.ValueSize() > ethdb.IdealBatchSize {
   271  			if err := batch.Write(); err != nil {
   272  				return err
   273  			}
   274  			batch.Reset()
   275  		}
   276  	}
   277  	if err := it.Error(); err != nil {
   278  		return err
   279  	}
   280  	return batch.Write()
   281  }