github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/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  // AncientRange returns an error as we don't have a backing chain freezer.
    93  func (db *nofreezedb) AncientRange(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  // Tail returns an error as we don't have a backing chain freezer.
   103  func (db *nofreezedb) Tail() (uint64, error) {
   104  	return 0, errNotSupported
   105  }
   106  
   107  // AncientSize returns an error as we don't have a backing chain freezer.
   108  func (db *nofreezedb) AncientSize(kind string) (uint64, error) {
   109  	return 0, errNotSupported
   110  }
   111  
   112  // ModifyAncients is not supported.
   113  func (db *nofreezedb) ModifyAncients(func(ethdb.AncientWriteOp) error) (int64, error) {
   114  	return 0, errNotSupported
   115  }
   116  
   117  // TruncateHead returns an error as we don't have a backing chain freezer.
   118  func (db *nofreezedb) TruncateHead(items uint64) error {
   119  	return errNotSupported
   120  }
   121  
   122  // TruncateTail returns an error as we don't have a backing chain freezer.
   123  func (db *nofreezedb) TruncateTail(items uint64) error {
   124  	return errNotSupported
   125  }
   126  
   127  // Sync returns an error as we don't have a backing chain freezer.
   128  func (db *nofreezedb) Sync() error {
   129  	return errNotSupported
   130  }
   131  
   132  func (db *nofreezedb) ReadAncients(fn func(reader ethdb.AncientReader) error) (err error) {
   133  	// Unlike other ancient-related methods, this method does not return
   134  	// errNotSupported when invoked.
   135  	// The reason for this is that the caller might want to do several things:
   136  	// 1. Check if something is in freezer,
   137  	// 2. If not, check leveldb.
   138  	//
   139  	// This will work, since the ancient-checks inside 'fn' will return errors,
   140  	// and the leveldb work will continue.
   141  	//
   142  	// If we instead were to return errNotSupported here, then the caller would
   143  	// have to explicitly check for that, having an extra clause to do the
   144  	// non-ancient operations.
   145  	return fn(db)
   146  }
   147  
   148  // MigrateTable processes the entries in a given table in sequence
   149  // converting them to a new format if they're of an old format.
   150  func (db *nofreezedb) MigrateTable(kind string, convert convertLegacyFn) error {
   151  	return errNotSupported
   152  }
   153  
   154  // NewDatabase creates a high level database on top of a given key-value data
   155  // store without a freezer moving immutable chain segments into cold storage.
   156  func NewDatabase(db ethdb.KeyValueStore) ethdb.Database {
   157  	return &nofreezedb{KeyValueStore: db}
   158  }
   159  
   160  // NewDatabaseWithFreezer creates a high level database on top of a given key-
   161  // value data store with a freezer moving immutable chain segments into cold
   162  // storage.
   163  func NewDatabaseWithFreezer(db ethdb.KeyValueStore, freezer string, namespace string, readonly bool) (ethdb.Database, error) {
   164  	// Create the idle freezer instance
   165  	frdb, err := newFreezer(freezer, namespace, readonly, freezerTableSize, FreezerNoSnappy)
   166  	if err != nil {
   167  		return nil, err
   168  	}
   169  	// Since the freezer can be stored separately from the user's key-value database,
   170  	// there's a fairly high probability that the user requests invalid combinations
   171  	// of the freezer and database. Ensure that we don't shoot ourselves in the foot
   172  	// by serving up conflicting data, leading to both datastores getting corrupted.
   173  	//
   174  	//   - If both the freezer and key-value store is empty (no genesis), we just
   175  	//     initialized a new empty freezer, so everything's fine.
   176  	//   - If the key-value store is empty, but the freezer is not, we need to make
   177  	//     sure the user's genesis matches the freezer. That will be checked in the
   178  	//     blockchain, since we don't have the genesis block here (nor should we at
   179  	//     this point care, the key-value/freezer combo is valid).
   180  	//   - If neither the key-value store nor the freezer is empty, cross validate
   181  	//     the genesis hashes to make sure they are compatible. If they are, also
   182  	//     ensure that there's no gap between the freezer and sunsequently leveldb.
   183  	//   - If the key-value store is not empty, but the freezer is we might just be
   184  	//     upgrading to the freezer release, or we might have had a small chain and
   185  	//     not frozen anything yet. Ensure that no blocks are missing yet from the
   186  	//     key-value store, since that would mean we already had an old freezer.
   187  
   188  	// If the genesis hash is empty, we have a new key-value store, so nothing to
   189  	// validate in this method. If, however, the genesis hash is not nil, compare
   190  	// it to the freezer content.
   191  	if kvgenesis, _ := db.Get(headerHashKey(0)); len(kvgenesis) > 0 {
   192  		if frozen, _ := frdb.Ancients(); frozen > 0 {
   193  			// If the freezer already contains something, ensure that the genesis blocks
   194  			// match, otherwise we might mix up freezers across chains and destroy both
   195  			// the freezer and the key-value store.
   196  			frgenesis, err := frdb.Ancient(freezerHashTable, 0)
   197  			if err != nil {
   198  				return nil, fmt.Errorf("failed to retrieve genesis from ancient %v", err)
   199  			} else if !bytes.Equal(kvgenesis, frgenesis) {
   200  				return nil, fmt.Errorf("genesis mismatch: %#x (leveldb) != %#x (ancients)", kvgenesis, frgenesis)
   201  			}
   202  			// Key-value store and freezer belong to the same network. Ensure that they
   203  			// are contiguous, otherwise we might end up with a non-functional freezer.
   204  			if kvhash, _ := db.Get(headerHashKey(frozen)); len(kvhash) == 0 {
   205  				// Subsequent header after the freezer limit is missing from the database.
   206  				// Reject startup is the database has a more recent head.
   207  				if *ReadHeaderNumber(db, ReadHeadHeaderHash(db)) > frozen-1 {
   208  					return nil, fmt.Errorf("gap (#%d) in the chain between ancients and leveldb", frozen)
   209  				}
   210  				// Database contains only older data than the freezer, this happens if the
   211  				// state was wiped and reinited from an existing freezer.
   212  			}
   213  			// Otherwise, key-value store continues where the freezer left off, all is fine.
   214  			// We might have duplicate blocks (crash after freezer write but before key-value
   215  			// store deletion, but that's fine).
   216  		} else {
   217  			// If the freezer is empty, ensure nothing was moved yet from the key-value
   218  			// store, otherwise we'll end up missing data. We check block #1 to decide
   219  			// if we froze anything previously or not, but do take care of databases with
   220  			// only the genesis block.
   221  			if ReadHeadHeaderHash(db) != common.BytesToHash(kvgenesis) {
   222  				// Key-value store contains more data than the genesis block, make sure we
   223  				// didn't freeze anything yet.
   224  				if kvblob, _ := db.Get(headerHashKey(1)); len(kvblob) == 0 {
   225  					return nil, errors.New("ancient chain segments already extracted, please set --datadir.ancient to the correct path")
   226  				}
   227  				// Block #1 is still in the database, we're allowed to init a new feezer
   228  			}
   229  			// Otherwise, the head header is still the genesis, we're allowed to init a new
   230  			// freezer.
   231  		}
   232  	}
   233  	// Freezer is consistent with the key-value database, permit combining the two
   234  	if !frdb.readonly {
   235  		frdb.wg.Add(1)
   236  		go func() {
   237  			frdb.freeze(db)
   238  			frdb.wg.Done()
   239  		}()
   240  	}
   241  	return &freezerdb{
   242  		KeyValueStore: db,
   243  		AncientStore:  frdb,
   244  	}, nil
   245  }
   246  
   247  // NewMemoryDatabase creates an ephemeral in-memory key-value database without a
   248  // freezer moving immutable chain segments into cold storage.
   249  func NewMemoryDatabase() ethdb.Database {
   250  	return NewDatabase(memorydb.New())
   251  }
   252  
   253  // NewMemoryDatabaseWithCap creates an ephemeral in-memory key-value database
   254  // with an initial starting capacity, but without a freezer moving immutable
   255  // chain segments into cold storage.
   256  func NewMemoryDatabaseWithCap(size int) ethdb.Database {
   257  	return NewDatabase(memorydb.NewWithCap(size))
   258  }
   259  
   260  // NewLevelDBDatabase creates a persistent key-value database without a freezer
   261  // moving immutable chain segments into cold storage.
   262  func NewLevelDBDatabase(file string, cache int, handles int, namespace string, readonly bool) (ethdb.Database, error) {
   263  	db, err := leveldb.New(file, cache, handles, namespace, readonly)
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  	return NewDatabase(db), nil
   268  }
   269  
   270  // NewLevelDBDatabaseWithFreezer creates a persistent key-value database with a
   271  // freezer moving immutable chain segments into cold storage.
   272  func NewLevelDBDatabaseWithFreezer(file string, cache int, handles int, freezer string, namespace string, readonly bool) (ethdb.Database, error) {
   273  	kvdb, err := leveldb.New(file, cache, handles, namespace, readonly)
   274  	if err != nil {
   275  		return nil, err
   276  	}
   277  	frdb, err := NewDatabaseWithFreezer(kvdb, freezer, namespace, readonly)
   278  	if err != nil {
   279  		kvdb.Close()
   280  		return nil, err
   281  	}
   282  	return frdb, nil
   283  }
   284  
   285  type counter uint64
   286  
   287  func (c counter) String() string {
   288  	return fmt.Sprintf("%d", c)
   289  }
   290  
   291  func (c counter) Percentage(current uint64) string {
   292  	return fmt.Sprintf("%d", current*100/uint64(c))
   293  }
   294  
   295  // stat stores sizes and count for a parameter
   296  type stat struct {
   297  	size  common.StorageSize
   298  	count counter
   299  }
   300  
   301  // Add size to the stat and increase the counter by 1
   302  func (s *stat) Add(size common.StorageSize) {
   303  	s.size += size
   304  	s.count++
   305  }
   306  
   307  func (s *stat) Size() string {
   308  	return s.size.String()
   309  }
   310  
   311  func (s *stat) Count() string {
   312  	return s.count.String()
   313  }
   314  
   315  // InspectDatabase traverses the entire database and checks the size
   316  // of all different categories of data.
   317  func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
   318  	it := db.NewIterator(keyPrefix, keyStart)
   319  	defer it.Release()
   320  
   321  	var (
   322  		count  int64
   323  		start  = time.Now()
   324  		logged = time.Now()
   325  
   326  		// Key-value store statistics
   327  		headers         stat
   328  		bodies          stat
   329  		receipts        stat
   330  		tds             stat
   331  		numHashPairings stat
   332  		hashNumPairings stat
   333  		tries           stat
   334  		codes           stat
   335  		txLookups       stat
   336  		accountSnaps    stat
   337  		storageSnaps    stat
   338  		preimages       stat
   339  		bloomBits       stat
   340  		beaconHeaders   stat
   341  		cliqueSnaps     stat
   342  
   343  		// Ancient store statistics
   344  		ancientHeadersSize  common.StorageSize
   345  		ancientBodiesSize   common.StorageSize
   346  		ancientReceiptsSize common.StorageSize
   347  		ancientTdsSize      common.StorageSize
   348  		ancientHashesSize   common.StorageSize
   349  
   350  		// Les statistic
   351  		chtTrieNodes   stat
   352  		bloomTrieNodes stat
   353  
   354  		// Meta- and unaccounted data
   355  		metadata    stat
   356  		unaccounted stat
   357  
   358  		// Totals
   359  		total common.StorageSize
   360  	)
   361  	// Inspect key-value database first.
   362  	for it.Next() {
   363  		var (
   364  			key  = it.Key()
   365  			size = common.StorageSize(len(key) + len(it.Value()))
   366  		)
   367  		total += size
   368  		switch {
   369  		case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength):
   370  			headers.Add(size)
   371  		case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength):
   372  			bodies.Add(size)
   373  		case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength):
   374  			receipts.Add(size)
   375  		case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix):
   376  			tds.Add(size)
   377  		case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix):
   378  			numHashPairings.Add(size)
   379  		case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
   380  			hashNumPairings.Add(size)
   381  		case len(key) == common.HashLength:
   382  			tries.Add(size)
   383  		case bytes.HasPrefix(key, CodePrefix) && len(key) == len(CodePrefix)+common.HashLength:
   384  			codes.Add(size)
   385  		case bytes.HasPrefix(key, txLookupPrefix) && len(key) == (len(txLookupPrefix)+common.HashLength):
   386  			txLookups.Add(size)
   387  		case bytes.HasPrefix(key, SnapshotAccountPrefix) && len(key) == (len(SnapshotAccountPrefix)+common.HashLength):
   388  			accountSnaps.Add(size)
   389  		case bytes.HasPrefix(key, SnapshotStoragePrefix) && len(key) == (len(SnapshotStoragePrefix)+2*common.HashLength):
   390  			storageSnaps.Add(size)
   391  		case bytes.HasPrefix(key, PreimagePrefix) && len(key) == (len(PreimagePrefix)+common.HashLength):
   392  			preimages.Add(size)
   393  		case bytes.HasPrefix(key, configPrefix) && len(key) == (len(configPrefix)+common.HashLength):
   394  			metadata.Add(size)
   395  		case bytes.HasPrefix(key, genesisPrefix) && len(key) == (len(genesisPrefix)+common.HashLength):
   396  			metadata.Add(size)
   397  		case bytes.HasPrefix(key, bloomBitsPrefix) && len(key) == (len(bloomBitsPrefix)+10+common.HashLength):
   398  			bloomBits.Add(size)
   399  		case bytes.HasPrefix(key, BloomBitsIndexPrefix):
   400  			bloomBits.Add(size)
   401  		case bytes.HasPrefix(key, skeletonHeaderPrefix) && len(key) == (len(skeletonHeaderPrefix)+8):
   402  			beaconHeaders.Add(size)
   403  		case bytes.HasPrefix(key, []byte("clique-")) && len(key) == 7+common.HashLength:
   404  			cliqueSnaps.Add(size)
   405  		case bytes.HasPrefix(key, []byte("cht-")) ||
   406  			bytes.HasPrefix(key, []byte("chtIndexV2-")) ||
   407  			bytes.HasPrefix(key, []byte("chtRootV2-")): // Canonical hash trie
   408  			chtTrieNodes.Add(size)
   409  		case bytes.HasPrefix(key, []byte("blt-")) ||
   410  			bytes.HasPrefix(key, []byte("bltIndex-")) ||
   411  			bytes.HasPrefix(key, []byte("bltRoot-")): // Bloomtrie sub
   412  			bloomTrieNodes.Add(size)
   413  		default:
   414  			var accounted bool
   415  			for _, meta := range [][]byte{
   416  				databaseVersionKey, headHeaderKey, headBlockKey, headFastBlockKey, lastPivotKey,
   417  				fastTrieProgressKey, snapshotDisabledKey, SnapshotRootKey, snapshotJournalKey,
   418  				snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey,
   419  				uncleanShutdownKey, badBlockKey, transitionStatusKey, skeletonSyncStatusKey,
   420  			} {
   421  				if bytes.Equal(key, meta) {
   422  					metadata.Add(size)
   423  					accounted = true
   424  					break
   425  				}
   426  			}
   427  			if !accounted {
   428  				unaccounted.Add(size)
   429  			}
   430  		}
   431  		count++
   432  		if count%1000 == 0 && time.Since(logged) > 8*time.Second {
   433  			log.Info("Inspecting database", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
   434  			logged = time.Now()
   435  		}
   436  	}
   437  	// Inspect append-only file store then.
   438  	ancientSizes := []*common.StorageSize{&ancientHeadersSize, &ancientBodiesSize, &ancientReceiptsSize, &ancientHashesSize, &ancientTdsSize}
   439  	for i, category := range []string{freezerHeaderTable, freezerBodiesTable, freezerReceiptTable, freezerHashTable, freezerDifficultyTable} {
   440  		if size, err := db.AncientSize(category); err == nil {
   441  			*ancientSizes[i] += common.StorageSize(size)
   442  			total += common.StorageSize(size)
   443  		}
   444  	}
   445  	// Get number of ancient rows inside the freezer
   446  	ancients := counter(0)
   447  	if count, err := db.Ancients(); err == nil {
   448  		ancients = counter(count)
   449  	}
   450  	// Display the database statistic.
   451  	stats := [][]string{
   452  		{"Key-Value store", "Headers", headers.Size(), headers.Count()},
   453  		{"Key-Value store", "Bodies", bodies.Size(), bodies.Count()},
   454  		{"Key-Value store", "Receipt lists", receipts.Size(), receipts.Count()},
   455  		{"Key-Value store", "Difficulties", tds.Size(), tds.Count()},
   456  		{"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()},
   457  		{"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()},
   458  		{"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()},
   459  		{"Key-Value store", "Bloombit index", bloomBits.Size(), bloomBits.Count()},
   460  		{"Key-Value store", "Contract codes", codes.Size(), codes.Count()},
   461  		{"Key-Value store", "Trie nodes", tries.Size(), tries.Count()},
   462  		{"Key-Value store", "Trie preimages", preimages.Size(), preimages.Count()},
   463  		{"Key-Value store", "Account snapshot", accountSnaps.Size(), accountSnaps.Count()},
   464  		{"Key-Value store", "Storage snapshot", storageSnaps.Size(), storageSnaps.Count()},
   465  		{"Key-Value store", "Beacon sync headers", beaconHeaders.Size(), beaconHeaders.Count()},
   466  		{"Key-Value store", "Clique snapshots", cliqueSnaps.Size(), cliqueSnaps.Count()},
   467  		{"Key-Value store", "Singleton metadata", metadata.Size(), metadata.Count()},
   468  		{"Ancient store", "Headers", ancientHeadersSize.String(), ancients.String()},
   469  		{"Ancient store", "Bodies", ancientBodiesSize.String(), ancients.String()},
   470  		{"Ancient store", "Receipt lists", ancientReceiptsSize.String(), ancients.String()},
   471  		{"Ancient store", "Difficulties", ancientTdsSize.String(), ancients.String()},
   472  		{"Ancient store", "Block number->hash", ancientHashesSize.String(), ancients.String()},
   473  		{"Light client", "CHT trie nodes", chtTrieNodes.Size(), chtTrieNodes.Count()},
   474  		{"Light client", "Bloom trie nodes", bloomTrieNodes.Size(), bloomTrieNodes.Count()},
   475  	}
   476  	table := tablewriter.NewWriter(os.Stdout)
   477  	table.SetHeader([]string{"Database", "Category", "Size", "Items"})
   478  	table.SetFooter([]string{"", "Total", total.String(), " "})
   479  	table.AppendBulk(stats)
   480  	table.Render()
   481  
   482  	if unaccounted.size > 0 {
   483  		log.Error("Database contains unaccounted data", "size", unaccounted.size, "count", unaccounted.count)
   484  	}
   485  
   486  	return nil
   487  }