github.com/MetalBlockchain/subnet-evm@v0.4.9/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/MetalBlockchain/subnet-evm/ethdb"
    36  	"github.com/MetalBlockchain/subnet-evm/ethdb/leveldb"
    37  	"github.com/MetalBlockchain/subnet-evm/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  		syncPerformed stat
   138  
   139  		// Les statistic
   140  		chtTrieNodes   stat
   141  		bloomTrieNodes stat
   142  
   143  		// Meta- and unaccounted data
   144  		metadata    stat
   145  		unaccounted stat
   146  
   147  		// Totals
   148  		total common.StorageSize
   149  	)
   150  	// Inspect key-value database first.
   151  	for it.Next() {
   152  		var (
   153  			key  = it.Key()
   154  			size = common.StorageSize(len(key) + len(it.Value()))
   155  		)
   156  		total += size
   157  		switch {
   158  		case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength):
   159  			headers.Add(size)
   160  		case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength):
   161  			bodies.Add(size)
   162  		case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength):
   163  			receipts.Add(size)
   164  		case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix):
   165  			numHashPairings.Add(size)
   166  		case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
   167  			hashNumPairings.Add(size)
   168  		case len(key) == common.HashLength:
   169  			tries.Add(size)
   170  		case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength:
   171  			codes.Add(size)
   172  		case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength):
   173  			txLookups.Add(size)
   174  		case bytes.HasPrefix(key, SnapshotAccountPrefix) && len(key) == (len(SnapshotAccountPrefix)+common.HashLength):
   175  			accountSnaps.Add(size)
   176  		case bytes.HasPrefix(key, SnapshotStoragePrefix) && len(key) == (len(SnapshotStoragePrefix)+2*common.HashLength):
   177  			storageSnaps.Add(size)
   178  		case bytes.HasPrefix(key, preimagePrefix) && len(key) == (len(preimagePrefix)+common.HashLength):
   179  			preimages.Add(size)
   180  		case bytes.HasPrefix(key, configPrefix) && len(key) == (len(configPrefix)+common.HashLength):
   181  			metadata.Add(size)
   182  		case bytes.HasPrefix(key, upgradeConfigPrefix) && len(key) == (len(upgradeConfigPrefix)+common.HashLength):
   183  			metadata.Add(size)
   184  		case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength):
   185  			bloomBits.Add(size)
   186  		case bytes.HasPrefix(key, BloomBitsIndexPrefix):
   187  			bloomBits.Add(size)
   188  		case bytes.HasPrefix(key, []byte("clique-")) && len(key) == 7+common.HashLength:
   189  			cliqueSnaps.Add(size)
   190  		case bytes.HasPrefix(key, []byte("cht-")) ||
   191  			bytes.HasPrefix(key, []byte("chtIndexV2-")) ||
   192  			bytes.HasPrefix(key, []byte("chtRootV2-")): // Canonical hash trie
   193  			chtTrieNodes.Add(size)
   194  		case bytes.HasPrefix(key, []byte("blt-")) ||
   195  			bytes.HasPrefix(key, []byte("bltIndex-")) ||
   196  			bytes.HasPrefix(key, []byte("bltRoot-")): // Bloomtrie sub
   197  			bloomTrieNodes.Add(size)
   198  		case bytes.HasPrefix(key, syncStorageTriesPrefix) && len(key) == syncStorageTriesKeyLength:
   199  			syncProgress.Add(size)
   200  		case bytes.HasPrefix(key, syncSegmentsPrefix) && len(key) == syncSegmentsKeyLength:
   201  			syncSegments.Add(size)
   202  		case bytes.HasPrefix(key, CodeToFetchPrefix) && len(key) == codeToFetchKeyLength:
   203  			codeToFetch.Add(size)
   204  		case bytes.HasPrefix(key, syncPerformedPrefix) && len(key) == syncPerformedKeyLength:
   205  			syncPerformed.Add(size)
   206  		default:
   207  			var accounted bool
   208  			for _, meta := range [][]byte{
   209  				databaseVersionKey, headHeaderKey, headBlockKey,
   210  				snapshotRootKey, snapshotBlockHashKey, snapshotGeneratorKey,
   211  				uncleanShutdownKey, syncRootKey, txIndexTailKey,
   212  			} {
   213  				if bytes.Equal(key, meta) {
   214  					metadata.Add(size)
   215  					accounted = true
   216  					break
   217  				}
   218  			}
   219  			if !accounted {
   220  				unaccounted.Add(size)
   221  			}
   222  		}
   223  		count++
   224  		if count%1000 == 0 && time.Since(logged) > 8*time.Second {
   225  			log.Info("Inspecting database", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
   226  			logged = time.Now()
   227  		}
   228  	}
   229  	// Display the database statistic.
   230  	stats := [][]string{
   231  		{"Key-Value store", "Headers", headers.Size(), headers.Count()},
   232  		{"Key-Value store", "Bodies", bodies.Size(), bodies.Count()},
   233  		{"Key-Value store", "Receipt lists", receipts.Size(), receipts.Count()},
   234  		{"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()},
   235  		{"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()},
   236  		{"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()},
   237  		{"Key-Value store", "Bloombit index", bloomBits.Size(), bloomBits.Count()},
   238  		{"Key-Value store", "Contract codes", codes.Size(), codes.Count()},
   239  		{"Key-Value store", "Trie nodes", tries.Size(), tries.Count()},
   240  		{"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()},
   241  		{"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()},
   242  		{"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()},
   243  		{"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()},
   244  		{"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()},
   245  		{"Light client", "CHT trie nodes", chtTrieNodes.Size(), chtTrieNodes.Count()},
   246  		{"Light client", "Bloom trie nodes", bloomTrieNodes.Size(), bloomTrieNodes.Count()},
   247  		{"State sync", "Trie segments", syncSegments.Size(), syncSegments.Count()},
   248  		{"State sync", "Storage tries to fetch", syncProgress.Size(), syncProgress.Count()},
   249  		{"State sync", "Code to fetch", codeToFetch.Size(), codeToFetch.Count()},
   250  		{"State sync", "Block numbers synced to", syncPerformed.Size(), syncPerformed.Count()},
   251  	}
   252  	table := tablewriter.NewWriter(os.Stdout)
   253  	table.SetHeader([]string{"Database", "Category", "Size", "Items"})
   254  	table.SetFooter([]string{"", "Total", total.String(), " "})
   255  	table.AppendBulk(stats)
   256  	table.Render()
   257  
   258  	if unaccounted.size > 0 {
   259  		log.Error("Database contains unaccounted data", "size", unaccounted.size, "count", unaccounted.count)
   260  	}
   261  
   262  	return nil
   263  }
   264  
   265  // ClearPrefix removes all keys in db that begin with prefix
   266  func ClearPrefix(db ethdb.KeyValueStore, prefix []byte) error {
   267  	it := db.NewIterator(prefix, nil)
   268  	defer it.Release()
   269  
   270  	batch := db.NewBatch()
   271  	for it.Next() {
   272  		key := common.CopyBytes(it.Key())
   273  		if err := batch.Delete(key); err != nil {
   274  			return err
   275  		}
   276  		if batch.ValueSize() > ethdb.IdealBatchSize {
   277  			if err := batch.Write(); err != nil {
   278  				return err
   279  			}
   280  			batch.Reset()
   281  		}
   282  	}
   283  	if err := it.Error(); err != nil {
   284  		return err
   285  	}
   286  	return batch.Write()
   287  }