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