github.com/shrimpyuk/bor@v0.2.15-0.20220224151350-fb4ec6020bae/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  	"sync/atomic"
    25  	"time"
    26  
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/ethereum/go-ethereum/ethdb"
    29  	"github.com/ethereum/go-ethereum/ethdb/leveldb"
    30  	"github.com/ethereum/go-ethereum/ethdb/memorydb"
    31  	"github.com/ethereum/go-ethereum/log"
    32  	"github.com/olekukonko/tablewriter"
    33  )
    34  
    35  // freezerdb is a database wrapper that enabled freezer data retrievals.
    36  type freezerdb struct {
    37  	ethdb.KeyValueStore
    38  	ethdb.AncientStore
    39  }
    40  
    41  // Close implements io.Closer, closing both the fast key-value store as well as
    42  // the slow ancient tables.
    43  func (frdb *freezerdb) Close() error {
    44  	var errs []error
    45  	if err := frdb.AncientStore.Close(); err != nil {
    46  		errs = append(errs, err)
    47  	}
    48  	if err := frdb.KeyValueStore.Close(); err != nil {
    49  		errs = append(errs, err)
    50  	}
    51  	if len(errs) != 0 {
    52  		return fmt.Errorf("%v", errs)
    53  	}
    54  	return nil
    55  }
    56  
    57  // Freeze is a helper method used for external testing to trigger and block until
    58  // a freeze cycle completes, without having to sleep for a minute to trigger the
    59  // automatic background run.
    60  func (frdb *freezerdb) Freeze(threshold uint64) error {
    61  	if frdb.AncientStore.(*freezer).readonly {
    62  		return errReadOnly
    63  	}
    64  	// Set the freezer threshold to a temporary value
    65  	defer func(old uint64) {
    66  		atomic.StoreUint64(&frdb.AncientStore.(*freezer).threshold, old)
    67  	}(atomic.LoadUint64(&frdb.AncientStore.(*freezer).threshold))
    68  	atomic.StoreUint64(&frdb.AncientStore.(*freezer).threshold, threshold)
    69  
    70  	// Trigger a freeze cycle and block until it's done
    71  	trigger := make(chan struct{}, 1)
    72  	frdb.AncientStore.(*freezer).trigger <- trigger
    73  	<-trigger
    74  	return nil
    75  }
    76  
    77  // nofreezedb is a database wrapper that disables freezer data retrievals.
    78  type nofreezedb struct {
    79  	ethdb.KeyValueStore
    80  }
    81  
    82  // HasAncient returns an error as we don't have a backing chain freezer.
    83  func (db *nofreezedb) HasAncient(kind string, number uint64) (bool, error) {
    84  	return false, errNotSupported
    85  }
    86  
    87  // Ancient returns an error as we don't have a backing chain freezer.
    88  func (db *nofreezedb) Ancient(kind string, number uint64) ([]byte, error) {
    89  	return nil, errNotSupported
    90  }
    91  
    92  // ReadAncients returns an error as we don't have a backing chain freezer.
    93  func (db *nofreezedb) ReadAncients(kind string, start, max, maxByteSize uint64) ([][]byte, error) {
    94  	return nil, errNotSupported
    95  }
    96  
    97  // Ancients returns an error as we don't have a backing chain freezer.
    98  func (db *nofreezedb) Ancients() (uint64, error) {
    99  	return 0, errNotSupported
   100  }
   101  
   102  // AncientSize returns an error as we don't have a backing chain freezer.
   103  func (db *nofreezedb) AncientSize(kind string) (uint64, error) {
   104  	return 0, errNotSupported
   105  }
   106  
   107  // ModifyAncients is not supported.
   108  func (db *nofreezedb) ModifyAncients(func(ethdb.AncientWriteOp) error) (int64, error) {
   109  	return 0, errNotSupported
   110  }
   111  
   112  // TruncateAncients returns an error as we don't have a backing chain freezer.
   113  func (db *nofreezedb) TruncateAncients(items uint64) error {
   114  	return errNotSupported
   115  }
   116  
   117  // Sync returns an error as we don't have a backing chain freezer.
   118  func (db *nofreezedb) Sync() error {
   119  	return errNotSupported
   120  }
   121  
   122  // NewDatabase creates a high level database on top of a given key-value data
   123  // store without a freezer moving immutable chain segments into cold storage.
   124  func NewDatabase(db ethdb.KeyValueStore) ethdb.Database {
   125  	return &nofreezedb{KeyValueStore: db}
   126  }
   127  
   128  // NewDatabaseWithFreezer creates a high level database on top of a given key-
   129  // value data store with a freezer moving immutable chain segments into cold
   130  // storage.
   131  func NewDatabaseWithFreezer(db ethdb.KeyValueStore, freezer string, namespace string, readonly bool) (ethdb.Database, error) {
   132  	// Create the idle freezer instance
   133  	frdb, err := newFreezer(freezer, namespace, readonly, freezerTableSize, FreezerNoSnappy)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	// Since the freezer can be stored separately from the user's key-value database,
   138  	// there's a fairly high probability that the user requests invalid combinations
   139  	// of the freezer and database. Ensure that we don't shoot ourselves in the foot
   140  	// by serving up conflicting data, leading to both datastores getting corrupted.
   141  	//
   142  	//   - If both the freezer and key-value store is empty (no genesis), we just
   143  	//     initialized a new empty freezer, so everything's fine.
   144  	//   - If the key-value store is empty, but the freezer is not, we need to make
   145  	//     sure the user's genesis matches the freezer. That will be checked in the
   146  	//     blockchain, since we don't have the genesis block here (nor should we at
   147  	//     this point care, the key-value/freezer combo is valid).
   148  	//   - If neither the key-value store nor the freezer is empty, cross validate
   149  	//     the genesis hashes to make sure they are compatible. If they are, also
   150  	//     ensure that there's no gap between the freezer and sunsequently leveldb.
   151  	//   - If the key-value store is not empty, but the freezer is we might just be
   152  	//     upgrading to the freezer release, or we might have had a small chain and
   153  	//     not frozen anything yet. Ensure that no blocks are missing yet from the
   154  	//     key-value store, since that would mean we already had an old freezer.
   155  
   156  	// If the genesis hash is empty, we have a new key-value store, so nothing to
   157  	// validate in this method. If, however, the genesis hash is not nil, compare
   158  	// it to the freezer content.
   159  	if kvgenesis, _ := db.Get(headerHashKey(0)); len(kvgenesis) > 0 {
   160  		if frozen, _ := frdb.Ancients(); frozen > 0 {
   161  			// If the freezer already contains something, ensure that the genesis blocks
   162  			// match, otherwise we might mix up freezers across chains and destroy both
   163  			// the freezer and the key-value store.
   164  			frgenesis, err := frdb.Ancient(freezerHashTable, 0)
   165  			if err != nil {
   166  				return nil, fmt.Errorf("failed to retrieve genesis from ancient %v", err)
   167  			} else if !bytes.Equal(kvgenesis, frgenesis) {
   168  				return nil, fmt.Errorf("genesis mismatch: %#x (leveldb) != %#x (ancients)", kvgenesis, frgenesis)
   169  			}
   170  			// Key-value store and freezer belong to the same network. Ensure that they
   171  			// are contiguous, otherwise we might end up with a non-functional freezer.
   172  			if kvhash, _ := db.Get(headerHashKey(frozen)); len(kvhash) == 0 {
   173  				// Subsequent header after the freezer limit is missing from the database.
   174  				// Reject startup is the database has a more recent head.
   175  				if *ReadHeaderNumber(db, ReadHeadHeaderHash(db)) > frozen-1 {
   176  					return nil, fmt.Errorf("gap (#%d) in the chain between ancients and leveldb", frozen)
   177  				}
   178  				// Database contains only older data than the freezer, this happens if the
   179  				// state was wiped and reinited from an existing freezer.
   180  			}
   181  			// Otherwise, key-value store continues where the freezer left off, all is fine.
   182  			// We might have duplicate blocks (crash after freezer write but before key-value
   183  			// store deletion, but that's fine).
   184  		} else {
   185  			// If the freezer is empty, ensure nothing was moved yet from the key-value
   186  			// store, otherwise we'll end up missing data. We check block #1 to decide
   187  			// if we froze anything previously or not, but do take care of databases with
   188  			// only the genesis block.
   189  			if ReadHeadHeaderHash(db) != common.BytesToHash(kvgenesis) {
   190  				// Key-value store contains more data than the genesis block, make sure we
   191  				// didn't freeze anything yet.
   192  				if kvblob, _ := db.Get(headerHashKey(1)); len(kvblob) == 0 {
   193  					return nil, errors.New("ancient chain segments already extracted, please set --datadir.ancient to the correct path")
   194  				}
   195  				// Block #1 is still in the database, we're allowed to init a new feezer
   196  			}
   197  			// Otherwise, the head header is still the genesis, we're allowed to init a new
   198  			// feezer.
   199  		}
   200  	}
   201  	// Freezer is consistent with the key-value database, permit combining the two
   202  	if !frdb.readonly {
   203  		frdb.wg.Add(1)
   204  		go func() {
   205  			frdb.freeze(db)
   206  			frdb.wg.Done()
   207  		}()
   208  	}
   209  	return &freezerdb{
   210  		KeyValueStore: db,
   211  		AncientStore:  frdb,
   212  	}, nil
   213  }
   214  
   215  // NewMemoryDatabase creates an ephemeral in-memory key-value database without a
   216  // freezer moving immutable chain segments into cold storage.
   217  func NewMemoryDatabase() ethdb.Database {
   218  	return NewDatabase(memorydb.New())
   219  }
   220  
   221  // NewMemoryDatabaseWithCap creates an ephemeral in-memory key-value database
   222  // with an initial starting capacity, but without a freezer moving immutable
   223  // chain segments into cold storage.
   224  func NewMemoryDatabaseWithCap(size int) ethdb.Database {
   225  	return NewDatabase(memorydb.NewWithCap(size))
   226  }
   227  
   228  // NewLevelDBDatabase creates a persistent key-value database without a freezer
   229  // moving immutable chain segments into cold storage.
   230  func NewLevelDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) {
   231  	db, err := leveldb.New(file, cache, handles, namespace, readonly)
   232  	if err != nil {
   233  		return nil, err
   234  	}
   235  	return NewDatabase(db), nil
   236  }
   237  
   238  // NewLevelDBDatabaseWithFreezer creates a persistent key-value database with a
   239  // freezer moving immutable chain segments into cold storage.
   240  func NewLevelDBDatabaseWithFreezer(file string, cache int, handles int, freezer string, namespace string, readonly bool) (ethdb.Database, error) {
   241  	kvdb, err := leveldb.New(file, cache, handles, namespace, readonly)
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  	frdb, err := NewDatabaseWithFreezer(kvdb, freezer, namespace, readonly)
   246  	if err != nil {
   247  		kvdb.Close()
   248  		return nil, err
   249  	}
   250  	return frdb, nil
   251  }
   252  
   253  type counter uint64
   254  
   255  func (c counter) String() string {
   256  	return fmt.Sprintf("%d", c)
   257  }
   258  
   259  func (c counter) Percentage(current uint64) string {
   260  	return fmt.Sprintf("%d", current*100/uint64(c))
   261  }
   262  
   263  // stat stores sizes and count for a parameter
   264  type stat struct {
   265  	size  common.StorageSize
   266  	count counter
   267  }
   268  
   269  // Add size to the stat and increase the counter by 1
   270  func (s *stat) Add(size common.StorageSize) {
   271  	s.size += size
   272  	s.count++
   273  }
   274  
   275  func (s *stat) Size() string {
   276  	return s.size.String()
   277  }
   278  
   279  func (s *stat) Count() string {
   280  	return s.count.String()
   281  }
   282  
   283  // InspectDatabase traverses the entire database and checks the size
   284  // of all different categories of data.
   285  func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
   286  	it := db.NewIterator(keyPrefix, keyStart)
   287  	defer it.Release()
   288  
   289  	var (
   290  		count  int64
   291  		start  = time.Now()
   292  		logged = time.Now()
   293  
   294  		// Key-value store statistics
   295  		headers         stat
   296  		bodies          stat
   297  		receipts        stat
   298  		tds             stat
   299  		numHashPairings stat
   300  		hashNumPairings stat
   301  		tries           stat
   302  		codes           stat
   303  		txLookups       stat
   304  		accountSnaps    stat
   305  		storageSnaps    stat
   306  		preimages       stat
   307  		bloomBits       stat
   308  		cliqueSnaps     stat
   309  
   310  		// Ancient store statistics
   311  		ancientHeadersSize  common.StorageSize
   312  		ancientBodiesSize   common.StorageSize
   313  		ancientReceiptsSize common.StorageSize
   314  		ancientTdsSize      common.StorageSize
   315  		ancientHashesSize   common.StorageSize
   316  
   317  		// Les statistic
   318  		chtTrieNodes   stat
   319  		bloomTrieNodes stat
   320  
   321  		// Meta- and unaccounted data
   322  		metadata    stat
   323  		unaccounted stat
   324  
   325  		// Totals
   326  		total common.StorageSize
   327  	)
   328  	// Inspect key-value database first.
   329  	for it.Next() {
   330  		var (
   331  			key  = it.Key()
   332  			size = common.StorageSize(len(key) + len(it.Value()))
   333  		)
   334  		total += size
   335  		switch {
   336  		case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength):
   337  			headers.Add(size)
   338  		case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength):
   339  			bodies.Add(size)
   340  		case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength):
   341  			receipts.Add(size)
   342  		case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix):
   343  			tds.Add(size)
   344  		case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix):
   345  			numHashPairings.Add(size)
   346  		case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
   347  			hashNumPairings.Add(size)
   348  		case len(key) == common.HashLength:
   349  			tries.Add(size)
   350  		case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength:
   351  			codes.Add(size)
   352  		case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength):
   353  			txLookups.Add(size)
   354  		case bytes.HasPrefix(key, SnapshotAccountPrefix) && len(key) == (len(SnapshotAccountPrefix)+common.HashLength):
   355  			accountSnaps.Add(size)
   356  		case bytes.HasPrefix(key, SnapshotStoragePrefix) && len(key) == (len(SnapshotStoragePrefix)+2*common.HashLength):
   357  			storageSnaps.Add(size)
   358  		case bytes.HasPrefix(key, preimagePrefix) && len(key) == (len(preimagePrefix)+common.HashLength):
   359  			preimages.Add(size)
   360  		case bytes.HasPrefix(key, configPrefix) && len(key) == (len(configPrefix)+common.HashLength):
   361  			metadata.Add(size)
   362  		case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength):
   363  			bloomBits.Add(size)
   364  		case bytes.HasPrefix(key, BloomBitsIndexPrefix):
   365  			bloomBits.Add(size)
   366  		case bytes.HasPrefix(key, []byte("clique-")) && len(key) == 7+common.HashLength:
   367  			cliqueSnaps.Add(size)
   368  		case bytes.HasPrefix(key, []byte("cht-")) ||
   369  			bytes.HasPrefix(key, []byte("chtIndexV2-")) ||
   370  			bytes.HasPrefix(key, []byte("chtRootV2-")): // Canonical hash trie
   371  			chtTrieNodes.Add(size)
   372  		case bytes.HasPrefix(key, []byte("blt-")) ||
   373  			bytes.HasPrefix(key, []byte("bltIndex-")) ||
   374  			bytes.HasPrefix(key, []byte("bltRoot-")): // Bloomtrie sub
   375  			bloomTrieNodes.Add(size)
   376  		default:
   377  			var accounted bool
   378  			for _, meta := range [][]byte{
   379  				databaseVersionKey, headHeaderKey, headBlockKey, headFastBlockKey, lastPivotKey,
   380  				fastTrieProgressKey, snapshotDisabledKey, snapshotRootKey, snapshotJournalKey,
   381  				snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey,
   382  				uncleanShutdownKey, badBlockKey,
   383  			} {
   384  				if bytes.Equal(key, meta) {
   385  					metadata.Add(size)
   386  					accounted = true
   387  					break
   388  				}
   389  			}
   390  			if !accounted {
   391  				unaccounted.Add(size)
   392  			}
   393  		}
   394  		count++
   395  		if count%1000 == 0 && time.Since(logged) > 8*time.Second {
   396  			log.Info("Inspecting database", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
   397  			logged = time.Now()
   398  		}
   399  	}
   400  	// Inspect append-only file store then.
   401  	ancientSizes := []*common.StorageSize{&ancientHeadersSize, &ancientBodiesSize, &ancientReceiptsSize, &ancientHashesSize, &ancientTdsSize}
   402  	for i, category := range []string{freezerHeaderTable, freezerBodiesTable, freezerReceiptTable, freezerHashTable, freezerDifficultyTable} {
   403  		if size, err := db.AncientSize(category); err == nil {
   404  			*ancientSizes[i] += common.StorageSize(size)
   405  			total += common.StorageSize(size)
   406  		}
   407  	}
   408  	// Get number of ancient rows inside the freezer
   409  	ancients := counter(0)
   410  	if count, err := db.Ancients(); err == nil {
   411  		ancients = counter(count)
   412  	}
   413  	// Display the database statistic.
   414  	stats := [][]string{
   415  		{"Key-Value store", "Headers", headers.Size(), headers.Count()},
   416  		{"Key-Value store", "Bodies", bodies.Size(), bodies.Count()},
   417  		{"Key-Value store", "Receipt lists", receipts.Size(), receipts.Count()},
   418  		{"Key-Value store", "Difficulties", tds.Size(), tds.Count()},
   419  		{"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()},
   420  		{"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()},
   421  		{"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()},
   422  		{"Key-Value store", "Bloombit index", bloomBits.Size(), bloomBits.Count()},
   423  		{"Key-Value store", "Contract codes", codes.Size(), codes.Count()},
   424  		{"Key-Value store", "Trie nodes", tries.Size(), tries.Count()},
   425  		{"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()},
   426  		{"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()},
   427  		{"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()},
   428  		{"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()},
   429  		{"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()},
   430  		{"Ancient store", "Headers", ancientHeadersSize.String(), ancients.String()},
   431  		{"Ancient store", "Bodies", ancientBodiesSize.String(), ancients.String()},
   432  		{"Ancient store", "Receipt lists", ancientReceiptsSize.String(), ancients.String()},
   433  		{"Ancient store", "Difficulties", ancientTdsSize.String(), ancients.String()},
   434  		{"Ancient store", "Block number->hash", ancientHashesSize.String(), ancients.String()},
   435  		{"Light client", "CHT trie nodes", chtTrieNodes.Size(), chtTrieNodes.Count()},
   436  		{"Light client", "Bloom trie nodes", bloomTrieNodes.Size(), bloomTrieNodes.Count()},
   437  	}
   438  	table := tablewriter.NewWriter(os.Stdout)
   439  	table.SetHeader([]string{"Database", "Category", "Size", "Items"})
   440  	table.SetFooter([]string{"", "Total", total.String(), " "})
   441  	table.AppendBulk(stats)
   442  	table.Render()
   443  
   444  	if unaccounted.size > 0 {
   445  		log.Error("Database contains unaccounted data", "size", unaccounted.size, "count", unaccounted.count)
   446  	}
   447  
   448  	return nil
   449  }