github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/cmd/geth/dbcmd.go (about)

     1  // Copyright 2020 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU 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  // go-ethereum 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 General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package main
    18  
    19  import (
    20  	"bytes"
    21  	"errors"
    22  	"fmt"
    23  	"os"
    24  	"os/signal"
    25  	"path/filepath"
    26  	"sort"
    27  	"strconv"
    28  	"strings"
    29  	"syscall"
    30  	"time"
    31  
    32  	"github.com/ethereum/go-ethereum/cmd/utils"
    33  	"github.com/ethereum/go-ethereum/common"
    34  	"github.com/ethereum/go-ethereum/common/hexutil"
    35  	"github.com/ethereum/go-ethereum/console/prompt"
    36  	"github.com/ethereum/go-ethereum/core/rawdb"
    37  	"github.com/ethereum/go-ethereum/core/state/snapshot"
    38  	"github.com/ethereum/go-ethereum/core/types"
    39  	"github.com/ethereum/go-ethereum/ethdb"
    40  	"github.com/ethereum/go-ethereum/log"
    41  	"github.com/ethereum/go-ethereum/trie"
    42  	"github.com/olekukonko/tablewriter"
    43  	"gopkg.in/urfave/cli.v1"
    44  )
    45  
    46  var (
    47  	removedbCommand = cli.Command{
    48  		Action:    utils.MigrateFlags(removeDB),
    49  		Name:      "removedb",
    50  		Usage:     "Remove blockchain and state databases",
    51  		ArgsUsage: "",
    52  		Flags: []cli.Flag{
    53  			utils.DataDirFlag,
    54  		},
    55  		Category: "DATABASE COMMANDS",
    56  		Description: `
    57  Remove blockchain and state databases`,
    58  	}
    59  	dbCommand = cli.Command{
    60  		Name:      "db",
    61  		Usage:     "Low level database operations",
    62  		ArgsUsage: "",
    63  		Category:  "DATABASE COMMANDS",
    64  		Subcommands: []cli.Command{
    65  			dbInspectCmd,
    66  			dbStatCmd,
    67  			dbCompactCmd,
    68  			dbGetCmd,
    69  			dbDeleteCmd,
    70  			dbPutCmd,
    71  			dbGetSlotsCmd,
    72  			dbDumpFreezerIndex,
    73  			dbImportCmd,
    74  			dbExportCmd,
    75  			dbMetadataCmd,
    76  			dbMigrateFreezerCmd,
    77  		},
    78  	}
    79  	dbInspectCmd = cli.Command{
    80  		Action:    utils.MigrateFlags(inspect),
    81  		Name:      "inspect",
    82  		ArgsUsage: "<prefix> <start>",
    83  		Flags: []cli.Flag{
    84  			utils.DataDirFlag,
    85  			utils.AncientFlag,
    86  			utils.SyncModeFlag,
    87  			utils.MainnetFlag,
    88  			utils.RopstenFlag,
    89  			utils.SepoliaFlag,
    90  			utils.RinkebyFlag,
    91  			utils.GoerliFlag,
    92  			utils.MumbaiFlag,
    93  			utils.BorMainnetFlag,
    94  		},
    95  		Usage:       "Inspect the storage size for each type of data in the database",
    96  		Description: `This commands iterates the entire database. If the optional 'prefix' and 'start' arguments are provided, then the iteration is limited to the given subset of data.`,
    97  	}
    98  	dbStatCmd = cli.Command{
    99  		Action: utils.MigrateFlags(dbStats),
   100  		Name:   "stats",
   101  		Usage:  "Print leveldb statistics",
   102  		Flags: []cli.Flag{
   103  			utils.DataDirFlag,
   104  			utils.SyncModeFlag,
   105  			utils.MainnetFlag,
   106  			utils.RopstenFlag,
   107  			utils.SepoliaFlag,
   108  			utils.RinkebyFlag,
   109  			utils.GoerliFlag,
   110  			utils.MumbaiFlag,
   111  			utils.BorMainnetFlag,
   112  		},
   113  	}
   114  	dbCompactCmd = cli.Command{
   115  		Action: utils.MigrateFlags(dbCompact),
   116  		Name:   "compact",
   117  		Usage:  "Compact leveldb database. WARNING: May take a very long time",
   118  		Flags: []cli.Flag{
   119  			utils.DataDirFlag,
   120  			utils.SyncModeFlag,
   121  			utils.MainnetFlag,
   122  			utils.RopstenFlag,
   123  			utils.SepoliaFlag,
   124  			utils.RinkebyFlag,
   125  			utils.GoerliFlag,
   126  			utils.MumbaiFlag,
   127  			utils.BorMainnetFlag,
   128  			utils.CacheFlag,
   129  			utils.CacheDatabaseFlag,
   130  		},
   131  		Description: `This command performs a database compaction. 
   132  WARNING: This operation may take a very long time to finish, and may cause database
   133  corruption if it is aborted during execution'!`,
   134  	}
   135  	dbGetCmd = cli.Command{
   136  		Action:    utils.MigrateFlags(dbGet),
   137  		Name:      "get",
   138  		Usage:     "Show the value of a database key",
   139  		ArgsUsage: "<hex-encoded key>",
   140  		Flags: []cli.Flag{
   141  			utils.DataDirFlag,
   142  			utils.SyncModeFlag,
   143  			utils.MainnetFlag,
   144  			utils.RopstenFlag,
   145  			utils.SepoliaFlag,
   146  			utils.RinkebyFlag,
   147  			utils.GoerliFlag,
   148  			utils.MumbaiFlag,
   149  			utils.BorMainnetFlag,
   150  		},
   151  		Description: "This command looks up the specified database key from the database.",
   152  	}
   153  	dbDeleteCmd = cli.Command{
   154  		Action:    utils.MigrateFlags(dbDelete),
   155  		Name:      "delete",
   156  		Usage:     "Delete a database key (WARNING: may corrupt your database)",
   157  		ArgsUsage: "<hex-encoded key>",
   158  		Flags: []cli.Flag{
   159  			utils.DataDirFlag,
   160  			utils.SyncModeFlag,
   161  			utils.MainnetFlag,
   162  			utils.RopstenFlag,
   163  			utils.SepoliaFlag,
   164  			utils.RinkebyFlag,
   165  			utils.GoerliFlag,
   166  			utils.MumbaiFlag,
   167  			utils.BorMainnetFlag,
   168  		},
   169  		Description: `This command deletes the specified database key from the database. 
   170  WARNING: This is a low-level operation which may cause database corruption!`,
   171  	}
   172  	dbPutCmd = cli.Command{
   173  		Action:    utils.MigrateFlags(dbPut),
   174  		Name:      "put",
   175  		Usage:     "Set the value of a database key (WARNING: may corrupt your database)",
   176  		ArgsUsage: "<hex-encoded key> <hex-encoded value>",
   177  		Flags: []cli.Flag{
   178  			utils.DataDirFlag,
   179  			utils.SyncModeFlag,
   180  			utils.MainnetFlag,
   181  			utils.RopstenFlag,
   182  			utils.SepoliaFlag,
   183  			utils.RinkebyFlag,
   184  			utils.GoerliFlag,
   185  			utils.MumbaiFlag,
   186  			utils.BorMainnetFlag,
   187  		},
   188  		Description: `This command sets a given database key to the given value. 
   189  WARNING: This is a low-level operation which may cause database corruption!`,
   190  	}
   191  	dbGetSlotsCmd = cli.Command{
   192  		Action:    utils.MigrateFlags(dbDumpTrie),
   193  		Name:      "dumptrie",
   194  		Usage:     "Show the storage key/values of a given storage trie",
   195  		ArgsUsage: "<hex-encoded storage trie root> <hex-encoded start (optional)> <int max elements (optional)>",
   196  		Flags: []cli.Flag{
   197  			utils.DataDirFlag,
   198  			utils.SyncModeFlag,
   199  			utils.MainnetFlag,
   200  			utils.RopstenFlag,
   201  			utils.SepoliaFlag,
   202  			utils.RinkebyFlag,
   203  			utils.GoerliFlag,
   204  			utils.MumbaiFlag,
   205  			utils.BorMainnetFlag,
   206  		},
   207  		Description: "This command looks up the specified database key from the database.",
   208  	}
   209  	dbDumpFreezerIndex = cli.Command{
   210  		Action:    utils.MigrateFlags(freezerInspect),
   211  		Name:      "freezer-index",
   212  		Usage:     "Dump out the index of a given freezer type",
   213  		ArgsUsage: "<type> <start (int)> <end (int)>",
   214  		Flags: []cli.Flag{
   215  			utils.DataDirFlag,
   216  			utils.SyncModeFlag,
   217  			utils.MainnetFlag,
   218  			utils.RopstenFlag,
   219  			utils.SepoliaFlag,
   220  			utils.RinkebyFlag,
   221  			utils.GoerliFlag,
   222  			utils.MumbaiFlag,
   223  			utils.BorMainnetFlag,
   224  		},
   225  		Description: "This command displays information about the freezer index.",
   226  	}
   227  	dbImportCmd = cli.Command{
   228  		Action:    utils.MigrateFlags(importLDBdata),
   229  		Name:      "import",
   230  		Usage:     "Imports leveldb-data from an exported RLP dump.",
   231  		ArgsUsage: "<dumpfile> <start (optional)",
   232  		Flags: []cli.Flag{
   233  			utils.DataDirFlag,
   234  			utils.SyncModeFlag,
   235  			utils.MainnetFlag,
   236  			utils.RopstenFlag,
   237  			utils.RinkebyFlag,
   238  			utils.GoerliFlag,
   239  		},
   240  		Description: "The import command imports the specific chain data from an RLP encoded stream.",
   241  	}
   242  	dbExportCmd = cli.Command{
   243  		Action:    utils.MigrateFlags(exportChaindata),
   244  		Name:      "export",
   245  		Usage:     "Exports the chain data into an RLP dump. If the <dumpfile> has .gz suffix, gzip compression will be used.",
   246  		ArgsUsage: "<type> <dumpfile>",
   247  		Flags: []cli.Flag{
   248  			utils.DataDirFlag,
   249  			utils.SyncModeFlag,
   250  			utils.MainnetFlag,
   251  			utils.RopstenFlag,
   252  			utils.RinkebyFlag,
   253  			utils.GoerliFlag,
   254  		},
   255  		Description: "Exports the specified chain data to an RLP encoded stream, optionally gzip-compressed.",
   256  	}
   257  	dbMetadataCmd = cli.Command{
   258  		Action: utils.MigrateFlags(showMetaData),
   259  		Name:   "metadata",
   260  		Usage:  "Shows metadata about the chain status.",
   261  		Flags: []cli.Flag{
   262  			utils.DataDirFlag,
   263  			utils.SyncModeFlag,
   264  			utils.MainnetFlag,
   265  			utils.RopstenFlag,
   266  			utils.SepoliaFlag,
   267  			utils.RinkebyFlag,
   268  			utils.GoerliFlag,
   269  		},
   270  		Description: "Shows metadata about the chain status.",
   271  	}
   272  	dbMigrateFreezerCmd = cli.Command{
   273  		Action:    utils.MigrateFlags(freezerMigrate),
   274  		Name:      "freezer-migrate",
   275  		Usage:     "Migrate legacy parts of the freezer. (WARNING: may take a long time)",
   276  		ArgsUsage: "",
   277  		Flags: []cli.Flag{
   278  			utils.DataDirFlag,
   279  			utils.SyncModeFlag,
   280  			utils.MainnetFlag,
   281  			utils.RopstenFlag,
   282  			utils.SepoliaFlag,
   283  			utils.RinkebyFlag,
   284  			utils.GoerliFlag,
   285  		},
   286  		Description: `The freezer-migrate command checks your database for receipts in a legacy format and updates those.
   287  WARNING: please back-up the receipt files in your ancients before running this command.`,
   288  	}
   289  )
   290  
   291  func removeDB(ctx *cli.Context) error {
   292  	stack, config := makeConfigNode(ctx)
   293  
   294  	// Remove the full node state database
   295  	path := stack.ResolvePath("chaindata")
   296  	if common.FileExist(path) {
   297  		confirmAndRemoveDB(path, "full node state database")
   298  	} else {
   299  		log.Info("Full node state database missing", "path", path)
   300  	}
   301  	// Remove the full node ancient database
   302  	path = config.Eth.DatabaseFreezer
   303  	switch {
   304  	case path == "":
   305  		path = filepath.Join(stack.ResolvePath("chaindata"), "ancient")
   306  	case !filepath.IsAbs(path):
   307  		path = config.Node.ResolvePath(path)
   308  	}
   309  	if common.FileExist(path) {
   310  		confirmAndRemoveDB(path, "full node ancient database")
   311  	} else {
   312  		log.Info("Full node ancient database missing", "path", path)
   313  	}
   314  	// Remove the light node database
   315  	path = stack.ResolvePath("lightchaindata")
   316  	if common.FileExist(path) {
   317  		confirmAndRemoveDB(path, "light node database")
   318  	} else {
   319  		log.Info("Light node database missing", "path", path)
   320  	}
   321  	return nil
   322  }
   323  
   324  // confirmAndRemoveDB prompts the user for a last confirmation and removes the
   325  // folder if accepted.
   326  func confirmAndRemoveDB(database string, kind string) {
   327  	confirm, err := prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database))
   328  	switch {
   329  	case err != nil:
   330  		utils.Fatalf("%v", err)
   331  	case !confirm:
   332  		log.Info("Database deletion skipped", "path", database)
   333  	default:
   334  		start := time.Now()
   335  		filepath.Walk(database, func(path string, info os.FileInfo, err error) error {
   336  			// If we're at the top level folder, recurse into
   337  			if path == database {
   338  				return nil
   339  			}
   340  			// Delete all the files, but not subfolders
   341  			if !info.IsDir() {
   342  				os.Remove(path)
   343  				return nil
   344  			}
   345  			return filepath.SkipDir
   346  		})
   347  		log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start)))
   348  	}
   349  }
   350  
   351  func inspect(ctx *cli.Context) error {
   352  	var (
   353  		prefix []byte
   354  		start  []byte
   355  	)
   356  	if ctx.NArg() > 2 {
   357  		return fmt.Errorf("Max 2 arguments: %v", ctx.Command.ArgsUsage)
   358  	}
   359  	if ctx.NArg() >= 1 {
   360  		if d, err := hexutil.Decode(ctx.Args().Get(0)); err != nil {
   361  			return fmt.Errorf("failed to hex-decode 'prefix': %v", err)
   362  		} else {
   363  			prefix = d
   364  		}
   365  	}
   366  	if ctx.NArg() >= 2 {
   367  		if d, err := hexutil.Decode(ctx.Args().Get(1)); err != nil {
   368  			return fmt.Errorf("failed to hex-decode 'start': %v", err)
   369  		} else {
   370  			start = d
   371  		}
   372  	}
   373  	stack, _ := makeConfigNode(ctx)
   374  	defer stack.Close()
   375  
   376  	db := utils.MakeChainDatabase(ctx, stack, true)
   377  	defer db.Close()
   378  
   379  	return rawdb.InspectDatabase(db, prefix, start)
   380  }
   381  
   382  func showLeveldbStats(db ethdb.Stater) {
   383  	if stats, err := db.Stat("leveldb.stats"); err != nil {
   384  		log.Warn("Failed to read database stats", "error", err)
   385  	} else {
   386  		fmt.Println(stats)
   387  	}
   388  	if ioStats, err := db.Stat("leveldb.iostats"); err != nil {
   389  		log.Warn("Failed to read database iostats", "error", err)
   390  	} else {
   391  		fmt.Println(ioStats)
   392  	}
   393  }
   394  
   395  func dbStats(ctx *cli.Context) error {
   396  	stack, _ := makeConfigNode(ctx)
   397  	defer stack.Close()
   398  
   399  	db := utils.MakeChainDatabase(ctx, stack, true)
   400  	defer db.Close()
   401  
   402  	showLeveldbStats(db)
   403  	return nil
   404  }
   405  
   406  func dbCompact(ctx *cli.Context) error {
   407  	stack, _ := makeConfigNode(ctx)
   408  	defer stack.Close()
   409  
   410  	db := utils.MakeChainDatabase(ctx, stack, false)
   411  	defer db.Close()
   412  
   413  	log.Info("Stats before compaction")
   414  	showLeveldbStats(db)
   415  
   416  	log.Info("Triggering compaction")
   417  	if err := db.Compact(nil, nil); err != nil {
   418  		log.Info("Compact err", "error", err)
   419  		return err
   420  	}
   421  	log.Info("Stats after compaction")
   422  	showLeveldbStats(db)
   423  	return nil
   424  }
   425  
   426  // dbGet shows the value of a given database key
   427  func dbGet(ctx *cli.Context) error {
   428  	if ctx.NArg() != 1 {
   429  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   430  	}
   431  	stack, _ := makeConfigNode(ctx)
   432  	defer stack.Close()
   433  
   434  	db := utils.MakeChainDatabase(ctx, stack, true)
   435  	defer db.Close()
   436  
   437  	key, err := parseHexOrString(ctx.Args().Get(0))
   438  	if err != nil {
   439  		log.Info("Could not decode the key", "error", err)
   440  		return err
   441  	}
   442  
   443  	data, err := db.Get(key)
   444  	if err != nil {
   445  		log.Info("Get operation failed", "key", fmt.Sprintf("0x%#x", key), "error", err)
   446  		return err
   447  	}
   448  	fmt.Printf("key %#x: %#x\n", key, data)
   449  	return nil
   450  }
   451  
   452  // dbDelete deletes a key from the database
   453  func dbDelete(ctx *cli.Context) error {
   454  	if ctx.NArg() != 1 {
   455  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   456  	}
   457  	stack, _ := makeConfigNode(ctx)
   458  	defer stack.Close()
   459  
   460  	db := utils.MakeChainDatabase(ctx, stack, false)
   461  	defer db.Close()
   462  
   463  	key, err := parseHexOrString(ctx.Args().Get(0))
   464  	if err != nil {
   465  		log.Info("Could not decode the key", "error", err)
   466  		return err
   467  	}
   468  	data, err := db.Get(key)
   469  	if err == nil {
   470  		fmt.Printf("Previous value: %#x\n", data)
   471  	}
   472  	if err = db.Delete(key); err != nil {
   473  		log.Info("Delete operation returned an error", "key", fmt.Sprintf("0x%#x", key), "error", err)
   474  		return err
   475  	}
   476  	return nil
   477  }
   478  
   479  // dbPut overwrite a value in the database
   480  func dbPut(ctx *cli.Context) error {
   481  	if ctx.NArg() != 2 {
   482  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   483  	}
   484  	stack, _ := makeConfigNode(ctx)
   485  	defer stack.Close()
   486  
   487  	db := utils.MakeChainDatabase(ctx, stack, false)
   488  	defer db.Close()
   489  
   490  	var (
   491  		key   []byte
   492  		value []byte
   493  		data  []byte
   494  		err   error
   495  	)
   496  	key, err = parseHexOrString(ctx.Args().Get(0))
   497  	if err != nil {
   498  		log.Info("Could not decode the key", "error", err)
   499  		return err
   500  	}
   501  	value, err = hexutil.Decode(ctx.Args().Get(1))
   502  	if err != nil {
   503  		log.Info("Could not decode the value", "error", err)
   504  		return err
   505  	}
   506  	data, err = db.Get(key)
   507  	if err == nil {
   508  		fmt.Printf("Previous value: %#x\n", data)
   509  	}
   510  	return db.Put(key, value)
   511  }
   512  
   513  // dbDumpTrie shows the key-value slots of a given storage trie
   514  func dbDumpTrie(ctx *cli.Context) error {
   515  	if ctx.NArg() < 1 {
   516  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   517  	}
   518  	stack, _ := makeConfigNode(ctx)
   519  	defer stack.Close()
   520  
   521  	db := utils.MakeChainDatabase(ctx, stack, true)
   522  	defer db.Close()
   523  	var (
   524  		root  []byte
   525  		start []byte
   526  		max   = int64(-1)
   527  		err   error
   528  	)
   529  	if root, err = hexutil.Decode(ctx.Args().Get(0)); err != nil {
   530  		log.Info("Could not decode the root", "error", err)
   531  		return err
   532  	}
   533  	stRoot := common.BytesToHash(root)
   534  	if ctx.NArg() >= 2 {
   535  		if start, err = hexutil.Decode(ctx.Args().Get(1)); err != nil {
   536  			log.Info("Could not decode the seek position", "error", err)
   537  			return err
   538  		}
   539  	}
   540  	if ctx.NArg() >= 3 {
   541  		if max, err = strconv.ParseInt(ctx.Args().Get(2), 10, 64); err != nil {
   542  			log.Info("Could not decode the max count", "error", err)
   543  			return err
   544  		}
   545  	}
   546  	theTrie, err := trie.New(stRoot, trie.NewDatabase(db))
   547  	if err != nil {
   548  		return err
   549  	}
   550  	var count int64
   551  	it := trie.NewIterator(theTrie.NodeIterator(start))
   552  	for it.Next() {
   553  		if max > 0 && count == max {
   554  			fmt.Printf("Exiting after %d values\n", count)
   555  			break
   556  		}
   557  		fmt.Printf("  %d. key %#x: %#x\n", count, it.Key, it.Value)
   558  		count++
   559  	}
   560  	return it.Err
   561  }
   562  
   563  func freezerInspect(ctx *cli.Context) error {
   564  	var (
   565  		start, end    int64
   566  		disableSnappy bool
   567  		err           error
   568  	)
   569  	if ctx.NArg() < 3 {
   570  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   571  	}
   572  	kind := ctx.Args().Get(0)
   573  	if noSnap, ok := rawdb.FreezerNoSnappy[kind]; !ok {
   574  		var options []string
   575  		for opt := range rawdb.FreezerNoSnappy {
   576  			options = append(options, opt)
   577  		}
   578  		sort.Strings(options)
   579  		return fmt.Errorf("Could read freezer-type '%v'. Available options: %v", kind, options)
   580  	} else {
   581  		disableSnappy = noSnap
   582  	}
   583  	if start, err = strconv.ParseInt(ctx.Args().Get(1), 10, 64); err != nil {
   584  		log.Info("Could read start-param", "error", err)
   585  		return err
   586  	}
   587  	if end, err = strconv.ParseInt(ctx.Args().Get(2), 10, 64); err != nil {
   588  		log.Info("Could read count param", "error", err)
   589  		return err
   590  	}
   591  	stack, _ := makeConfigNode(ctx)
   592  	defer stack.Close()
   593  	path := filepath.Join(stack.ResolvePath("chaindata"), "ancient")
   594  	log.Info("Opening freezer", "location", path, "name", kind)
   595  	if f, err := rawdb.NewFreezerTable(path, kind, disableSnappy, true); err != nil {
   596  		return err
   597  	} else {
   598  		f.DumpIndex(start, end)
   599  	}
   600  	return nil
   601  }
   602  
   603  // ParseHexOrString tries to hexdecode b, but if the prefix is missing, it instead just returns the raw bytes
   604  func parseHexOrString(str string) ([]byte, error) {
   605  	b, err := hexutil.Decode(str)
   606  	if errors.Is(err, hexutil.ErrMissingPrefix) {
   607  		return []byte(str), nil
   608  	}
   609  	return b, err
   610  }
   611  
   612  func importLDBdata(ctx *cli.Context) error {
   613  	start := 0
   614  	switch ctx.NArg() {
   615  	case 1:
   616  		break
   617  	case 2:
   618  		s, err := strconv.Atoi(ctx.Args().Get(1))
   619  		if err != nil {
   620  			return fmt.Errorf("second arg must be an integer: %v", err)
   621  		}
   622  		start = s
   623  	default:
   624  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   625  	}
   626  	var (
   627  		fName     = ctx.Args().Get(0)
   628  		stack, _  = makeConfigNode(ctx)
   629  		interrupt = make(chan os.Signal, 1)
   630  		stop      = make(chan struct{})
   631  	)
   632  	defer stack.Close()
   633  	signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
   634  	defer signal.Stop(interrupt)
   635  	defer close(interrupt)
   636  	go func() {
   637  		if _, ok := <-interrupt; ok {
   638  			log.Info("Interrupted during ldb import, stopping at next batch")
   639  		}
   640  		close(stop)
   641  	}()
   642  	db := utils.MakeChainDatabase(ctx, stack, false)
   643  	return utils.ImportLDBData(db, fName, int64(start), stop)
   644  }
   645  
   646  type preimageIterator struct {
   647  	iter ethdb.Iterator
   648  }
   649  
   650  func (iter *preimageIterator) Next() (byte, []byte, []byte, bool) {
   651  	for iter.iter.Next() {
   652  		key := iter.iter.Key()
   653  		if bytes.HasPrefix(key, rawdb.PreimagePrefix) && len(key) == (len(rawdb.PreimagePrefix)+common.HashLength) {
   654  			return utils.OpBatchAdd, key, iter.iter.Value(), true
   655  		}
   656  	}
   657  	return 0, nil, nil, false
   658  }
   659  
   660  func (iter *preimageIterator) Release() {
   661  	iter.iter.Release()
   662  }
   663  
   664  type snapshotIterator struct {
   665  	init    bool
   666  	account ethdb.Iterator
   667  	storage ethdb.Iterator
   668  }
   669  
   670  func (iter *snapshotIterator) Next() (byte, []byte, []byte, bool) {
   671  	if !iter.init {
   672  		iter.init = true
   673  		return utils.OpBatchDel, rawdb.SnapshotRootKey, nil, true
   674  	}
   675  	for iter.account.Next() {
   676  		key := iter.account.Key()
   677  		if bytes.HasPrefix(key, rawdb.SnapshotAccountPrefix) && len(key) == (len(rawdb.SnapshotAccountPrefix)+common.HashLength) {
   678  			return utils.OpBatchAdd, key, iter.account.Value(), true
   679  		}
   680  	}
   681  	for iter.storage.Next() {
   682  		key := iter.storage.Key()
   683  		if bytes.HasPrefix(key, rawdb.SnapshotStoragePrefix) && len(key) == (len(rawdb.SnapshotStoragePrefix)+2*common.HashLength) {
   684  			return utils.OpBatchAdd, key, iter.storage.Value(), true
   685  		}
   686  	}
   687  	return 0, nil, nil, false
   688  }
   689  
   690  func (iter *snapshotIterator) Release() {
   691  	iter.account.Release()
   692  	iter.storage.Release()
   693  }
   694  
   695  // chainExporters defines the export scheme for all exportable chain data.
   696  var chainExporters = map[string]func(db ethdb.Database) utils.ChainDataIterator{
   697  	"preimage": func(db ethdb.Database) utils.ChainDataIterator {
   698  		iter := db.NewIterator(rawdb.PreimagePrefix, nil)
   699  		return &preimageIterator{iter: iter}
   700  	},
   701  	"snapshot": func(db ethdb.Database) utils.ChainDataIterator {
   702  		account := db.NewIterator(rawdb.SnapshotAccountPrefix, nil)
   703  		storage := db.NewIterator(rawdb.SnapshotStoragePrefix, nil)
   704  		return &snapshotIterator{account: account, storage: storage}
   705  	},
   706  }
   707  
   708  func exportChaindata(ctx *cli.Context) error {
   709  	if ctx.NArg() < 2 {
   710  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   711  	}
   712  	// Parse the required chain data type, make sure it's supported.
   713  	kind := ctx.Args().Get(0)
   714  	kind = strings.ToLower(strings.Trim(kind, " "))
   715  	exporter, ok := chainExporters[kind]
   716  	if !ok {
   717  		var kinds []string
   718  		for kind := range chainExporters {
   719  			kinds = append(kinds, kind)
   720  		}
   721  		return fmt.Errorf("invalid data type %s, supported types: %s", kind, strings.Join(kinds, ", "))
   722  	}
   723  	var (
   724  		stack, _  = makeConfigNode(ctx)
   725  		interrupt = make(chan os.Signal, 1)
   726  		stop      = make(chan struct{})
   727  	)
   728  	defer stack.Close()
   729  	signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
   730  	defer signal.Stop(interrupt)
   731  	defer close(interrupt)
   732  	go func() {
   733  		if _, ok := <-interrupt; ok {
   734  			log.Info("Interrupted during db export, stopping at next batch")
   735  		}
   736  		close(stop)
   737  	}()
   738  	db := utils.MakeChainDatabase(ctx, stack, true)
   739  	return utils.ExportChaindata(ctx.Args().Get(1), kind, exporter(db), stop)
   740  }
   741  
   742  func showMetaData(ctx *cli.Context) error {
   743  	stack, _ := makeConfigNode(ctx)
   744  	defer stack.Close()
   745  	db := utils.MakeChainDatabase(ctx, stack, true)
   746  	ancients, err := db.Ancients()
   747  	if err != nil {
   748  		fmt.Fprintf(os.Stderr, "Error accessing ancients: %v", err)
   749  	}
   750  	pp := func(val *uint64) string {
   751  		if val == nil {
   752  			return "<nil>"
   753  		}
   754  		return fmt.Sprintf("%d (0x%x)", *val, *val)
   755  	}
   756  	data := [][]string{
   757  		{"databaseVersion", pp(rawdb.ReadDatabaseVersion(db))},
   758  		{"headBlockHash", fmt.Sprintf("%v", rawdb.ReadHeadBlockHash(db))},
   759  		{"headFastBlockHash", fmt.Sprintf("%v", rawdb.ReadHeadFastBlockHash(db))},
   760  		{"headHeaderHash", fmt.Sprintf("%v", rawdb.ReadHeadHeaderHash(db))}}
   761  	if b := rawdb.ReadHeadBlock(db); b != nil {
   762  		data = append(data, []string{"headBlock.Hash", fmt.Sprintf("%v", b.Hash())})
   763  		data = append(data, []string{"headBlock.Root", fmt.Sprintf("%v", b.Root())})
   764  		data = append(data, []string{"headBlock.Number", fmt.Sprintf("%d (0x%x)", b.Number(), b.Number())})
   765  	}
   766  	if b := rawdb.ReadSkeletonSyncStatus(db); b != nil {
   767  		data = append(data, []string{"SkeletonSyncStatus", string(b)})
   768  	}
   769  	if h := rawdb.ReadHeadHeader(db); h != nil {
   770  		data = append(data, []string{"headHeader.Hash", fmt.Sprintf("%v", h.Hash())})
   771  		data = append(data, []string{"headHeader.Root", fmt.Sprintf("%v", h.Root)})
   772  		data = append(data, []string{"headHeader.Number", fmt.Sprintf("%d (0x%x)", h.Number, h.Number)})
   773  	}
   774  	data = append(data, [][]string{{"frozen", fmt.Sprintf("%d items", ancients)},
   775  		{"lastPivotNumber", pp(rawdb.ReadLastPivotNumber(db))},
   776  		{"len(snapshotSyncStatus)", fmt.Sprintf("%d bytes", len(rawdb.ReadSnapshotSyncStatus(db)))},
   777  		{"snapshotGenerator", snapshot.ParseGeneratorStatus(rawdb.ReadSnapshotGenerator(db))},
   778  		{"snapshotDisabled", fmt.Sprintf("%v", rawdb.ReadSnapshotDisabled(db))},
   779  		{"snapshotJournal", fmt.Sprintf("%d bytes", len(rawdb.ReadSnapshotJournal(db)))},
   780  		{"snapshotRecoveryNumber", pp(rawdb.ReadSnapshotRecoveryNumber(db))},
   781  		{"snapshotRoot", fmt.Sprintf("%v", rawdb.ReadSnapshotRoot(db))},
   782  		{"txIndexTail", pp(rawdb.ReadTxIndexTail(db))},
   783  		{"fastTxLookupLimit", pp(rawdb.ReadFastTxLookupLimit(db))},
   784  	}...)
   785  	table := tablewriter.NewWriter(os.Stdout)
   786  	table.SetHeader([]string{"Field", "Value"})
   787  	table.AppendBulk(data)
   788  	table.Render()
   789  	return nil
   790  }
   791  
   792  func freezerMigrate(ctx *cli.Context) error {
   793  	stack, _ := makeConfigNode(ctx)
   794  	defer stack.Close()
   795  
   796  	db := utils.MakeChainDatabase(ctx, stack, false)
   797  	defer db.Close()
   798  
   799  	// Check first block for legacy receipt format
   800  	numAncients, err := db.Ancients()
   801  	if err != nil {
   802  		return err
   803  	}
   804  	if numAncients < 1 {
   805  		log.Info("No receipts in freezer to migrate")
   806  		return nil
   807  	}
   808  
   809  	isFirstLegacy, firstIdx, err := dbHasLegacyReceipts(db, 0)
   810  	if err != nil {
   811  		return err
   812  	}
   813  	if !isFirstLegacy {
   814  		log.Info("No legacy receipts to migrate")
   815  		return nil
   816  	}
   817  
   818  	log.Info("Starting migration", "ancients", numAncients, "firstLegacy", firstIdx)
   819  	start := time.Now()
   820  	if err := db.MigrateTable("receipts", types.ConvertLegacyStoredReceipts); err != nil {
   821  		return err
   822  	}
   823  	if err := db.Close(); err != nil {
   824  		return err
   825  	}
   826  	log.Info("Migration finished", "duration", time.Since(start))
   827  
   828  	return nil
   829  }
   830  
   831  // dbHasLegacyReceipts checks freezer entries for legacy receipts. It stops at the first
   832  // non-empty receipt and checks its format. The index of this first non-empty element is
   833  // the second return parameter.
   834  func dbHasLegacyReceipts(db ethdb.Database, firstIdx uint64) (bool, uint64, error) {
   835  	// Check first block for legacy receipt format
   836  	numAncients, err := db.Ancients()
   837  	if err != nil {
   838  		return false, 0, err
   839  	}
   840  	if numAncients < 1 {
   841  		return false, 0, nil
   842  	}
   843  	if firstIdx >= numAncients {
   844  		return false, firstIdx, nil
   845  	}
   846  	var (
   847  		legacy       bool
   848  		blob         []byte
   849  		emptyRLPList = []byte{192}
   850  	)
   851  	// Find first block with non-empty receipt, only if
   852  	// the index is not already provided.
   853  	if firstIdx == 0 {
   854  		for i := uint64(0); i < numAncients; i++ {
   855  			blob, err = db.Ancient("receipts", i)
   856  			if err != nil {
   857  				return false, 0, err
   858  			}
   859  			if len(blob) == 0 {
   860  				continue
   861  			}
   862  			if !bytes.Equal(blob, emptyRLPList) {
   863  				firstIdx = i
   864  				break
   865  			}
   866  		}
   867  	}
   868  	// Is first non-empty receipt legacy?
   869  	first, err := db.Ancient("receipts", firstIdx)
   870  	if err != nil {
   871  		return false, 0, err
   872  	}
   873  	legacy, err = types.IsLegacyStoredReceipts(first)
   874  	return legacy, firstIdx, err
   875  }