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