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