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