github.com/cryptotooltop/go-ethereum@v0.0.0-20231103184714-151d1922f3e5/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  	"gopkg.in/urfave/cli.v1"
    33  
    34  	"github.com/scroll-tech/go-ethereum/cmd/utils"
    35  	"github.com/scroll-tech/go-ethereum/common"
    36  	"github.com/scroll-tech/go-ethereum/common/hexutil"
    37  	"github.com/scroll-tech/go-ethereum/console/prompt"
    38  	"github.com/scroll-tech/go-ethereum/core/rawdb"
    39  	"github.com/scroll-tech/go-ethereum/ethdb"
    40  	"github.com/scroll-tech/go-ethereum/log"
    41  	"github.com/scroll-tech/go-ethereum/trie"
    42  )
    43  
    44  var (
    45  	removedbCommand = cli.Command{
    46  		Action:    utils.MigrateFlags(removeDB),
    47  		Name:      "removedb",
    48  		Usage:     "Remove blockchain and state databases",
    49  		ArgsUsage: "",
    50  		Flags: []cli.Flag{
    51  			utils.DataDirFlag,
    52  		},
    53  		Category: "DATABASE COMMANDS",
    54  		Description: `
    55  Remove blockchain and state databases`,
    56  	}
    57  	dbCommand = cli.Command{
    58  		Name:      "db",
    59  		Usage:     "Low level database operations",
    60  		ArgsUsage: "",
    61  		Category:  "DATABASE COMMANDS",
    62  		Subcommands: []cli.Command{
    63  			dbInspectCmd,
    64  			dbStatCmd,
    65  			dbCompactCmd,
    66  			dbGetCmd,
    67  			dbDeleteCmd,
    68  			dbPutCmd,
    69  			dbGetSlotsCmd,
    70  			dbDumpFreezerIndex,
    71  			dbImportCmd,
    72  			dbExportCmd,
    73  		},
    74  	}
    75  	dbInspectCmd = cli.Command{
    76  		Action:    utils.MigrateFlags(inspect),
    77  		Name:      "inspect",
    78  		ArgsUsage: "<prefix> <start>",
    79  		Flags: []cli.Flag{
    80  			utils.DataDirFlag,
    81  			utils.AncientFlag,
    82  			utils.SyncModeFlag,
    83  			utils.MainnetFlag,
    84  			utils.RopstenFlag,
    85  			utils.SepoliaFlag,
    86  			utils.RinkebyFlag,
    87  			utils.GoerliFlag,
    88  			utils.ScrollAlphaFlag,
    89  			utils.ScrollSepoliaFlag,
    90  			utils.ScrollFlag,
    91  		},
    92  		Usage:       "Inspect the storage size for each type of data in the database",
    93  		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.`,
    94  	}
    95  	dbStatCmd = cli.Command{
    96  		Action: utils.MigrateFlags(dbStats),
    97  		Name:   "stats",
    98  		Usage:  "Print leveldb statistics",
    99  		Flags: []cli.Flag{
   100  			utils.DataDirFlag,
   101  			utils.SyncModeFlag,
   102  			utils.MainnetFlag,
   103  			utils.RopstenFlag,
   104  			utils.SepoliaFlag,
   105  			utils.RinkebyFlag,
   106  			utils.GoerliFlag,
   107  			utils.ScrollAlphaFlag,
   108  			utils.ScrollSepoliaFlag,
   109  			utils.ScrollFlag,
   110  		},
   111  	}
   112  	dbCompactCmd = cli.Command{
   113  		Action: utils.MigrateFlags(dbCompact),
   114  		Name:   "compact",
   115  		Usage:  "Compact leveldb database. WARNING: May take a very long time",
   116  		Flags: []cli.Flag{
   117  			utils.DataDirFlag,
   118  			utils.SyncModeFlag,
   119  			utils.MainnetFlag,
   120  			utils.RopstenFlag,
   121  			utils.SepoliaFlag,
   122  			utils.RinkebyFlag,
   123  			utils.GoerliFlag,
   124  			utils.ScrollAlphaFlag,
   125  			utils.ScrollSepoliaFlag,
   126  			utils.ScrollFlag,
   127  			utils.CacheFlag,
   128  			utils.CacheDatabaseFlag,
   129  		},
   130  		Description: `This command performs a database compaction.
   131  WARNING: This operation may take a very long time to finish, and may cause database
   132  corruption if it is aborted during execution'!`,
   133  	}
   134  	dbGetCmd = cli.Command{
   135  		Action:    utils.MigrateFlags(dbGet),
   136  		Name:      "get",
   137  		Usage:     "Show the value of a database key",
   138  		ArgsUsage: "<hex-encoded key>",
   139  		Flags: []cli.Flag{
   140  			utils.DataDirFlag,
   141  			utils.SyncModeFlag,
   142  			utils.MainnetFlag,
   143  			utils.RopstenFlag,
   144  			utils.SepoliaFlag,
   145  			utils.RinkebyFlag,
   146  			utils.GoerliFlag,
   147  			utils.ScrollAlphaFlag,
   148  			utils.ScrollSepoliaFlag,
   149  			utils.ScrollFlag,
   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.ScrollAlphaFlag,
   167  			utils.ScrollSepoliaFlag,
   168  			utils.ScrollFlag,
   169  		},
   170  		Description: `This command deletes the specified database key from the database.
   171  WARNING: This is a low-level operation which may cause database corruption!`,
   172  	}
   173  	dbPutCmd = cli.Command{
   174  		Action:    utils.MigrateFlags(dbPut),
   175  		Name:      "put",
   176  		Usage:     "Set the value of a database key (WARNING: may corrupt your database)",
   177  		ArgsUsage: "<hex-encoded key> <hex-encoded value>",
   178  		Flags: []cli.Flag{
   179  			utils.DataDirFlag,
   180  			utils.SyncModeFlag,
   181  			utils.MainnetFlag,
   182  			utils.RopstenFlag,
   183  			utils.SepoliaFlag,
   184  			utils.RinkebyFlag,
   185  			utils.GoerliFlag,
   186  			utils.ScrollAlphaFlag,
   187  			utils.ScrollSepoliaFlag,
   188  			utils.ScrollFlag,
   189  		},
   190  		Description: `This command sets a given database key to the given value.
   191  WARNING: This is a low-level operation which may cause database corruption!`,
   192  	}
   193  	dbGetSlotsCmd = cli.Command{
   194  		Action:    utils.MigrateFlags(dbDumpTrie),
   195  		Name:      "dumptrie",
   196  		Usage:     "Show the storage key/values of a given storage trie",
   197  		ArgsUsage: "<hex-encoded storage trie root> <hex-encoded start (optional)> <int max elements (optional)>",
   198  		Flags: []cli.Flag{
   199  			utils.DataDirFlag,
   200  			utils.SyncModeFlag,
   201  			utils.MainnetFlag,
   202  			utils.RopstenFlag,
   203  			utils.SepoliaFlag,
   204  			utils.RinkebyFlag,
   205  			utils.GoerliFlag,
   206  			utils.ScrollAlphaFlag,
   207  			utils.ScrollSepoliaFlag,
   208  			utils.ScrollFlag,
   209  		},
   210  		Description: "This command looks up the specified database key from the database.",
   211  	}
   212  	dbDumpFreezerIndex = cli.Command{
   213  		Action:    utils.MigrateFlags(freezerInspect),
   214  		Name:      "freezer-index",
   215  		Usage:     "Dump out the index of a given freezer type",
   216  		ArgsUsage: "<type> <start (int)> <end (int)>",
   217  		Flags: []cli.Flag{
   218  			utils.DataDirFlag,
   219  			utils.SyncModeFlag,
   220  			utils.MainnetFlag,
   221  			utils.RopstenFlag,
   222  			utils.SepoliaFlag,
   223  			utils.RinkebyFlag,
   224  			utils.GoerliFlag,
   225  			utils.ScrollAlphaFlag,
   226  			utils.ScrollSepoliaFlag,
   227  			utils.ScrollFlag,
   228  		},
   229  		Description: "This command displays information about the freezer index.",
   230  	}
   231  	dbImportCmd = cli.Command{
   232  		Action:    utils.MigrateFlags(importLDBdata),
   233  		Name:      "import",
   234  		Usage:     "Imports leveldb-data from an exported RLP dump.",
   235  		ArgsUsage: "<dumpfile> <start (optional)",
   236  		Flags: []cli.Flag{
   237  			utils.DataDirFlag,
   238  			utils.SyncModeFlag,
   239  			utils.MainnetFlag,
   240  			utils.RopstenFlag,
   241  			utils.RinkebyFlag,
   242  			utils.GoerliFlag,
   243  			utils.ScrollAlphaFlag,
   244  			utils.ScrollSepoliaFlag,
   245  			utils.ScrollFlag,
   246  		},
   247  		Description: "The import command imports the specific chain data from an RLP encoded stream.",
   248  	}
   249  	dbExportCmd = cli.Command{
   250  		Action:    utils.MigrateFlags(exportChaindata),
   251  		Name:      "export",
   252  		Usage:     "Exports the chain data into an RLP dump. If the <dumpfile> has .gz suffix, gzip compression will be used.",
   253  		ArgsUsage: "<type> <dumpfile>",
   254  		Flags: []cli.Flag{
   255  			utils.DataDirFlag,
   256  			utils.SyncModeFlag,
   257  			utils.MainnetFlag,
   258  			utils.RopstenFlag,
   259  			utils.RinkebyFlag,
   260  			utils.GoerliFlag,
   261  			utils.ScrollAlphaFlag,
   262  			utils.ScrollSepoliaFlag,
   263  			utils.ScrollFlag,
   264  		},
   265  		Description: "Exports the specified chain data to an RLP encoded stream, optionally gzip-compressed.",
   266  	}
   267  )
   268  
   269  func removeDB(ctx *cli.Context) error {
   270  	stack, config := makeConfigNode(ctx)
   271  
   272  	// Remove the full node state database
   273  	path := stack.ResolvePath("chaindata")
   274  	if common.FileExist(path) {
   275  		confirmAndRemoveDB(path, "full node state database")
   276  	} else {
   277  		log.Info("Full node state database missing", "path", path)
   278  	}
   279  	// Remove the full node ancient database
   280  	path = config.Eth.DatabaseFreezer
   281  	switch {
   282  	case path == "":
   283  		path = filepath.Join(stack.ResolvePath("chaindata"), "ancient")
   284  	case !filepath.IsAbs(path):
   285  		path = config.Node.ResolvePath(path)
   286  	}
   287  	if common.FileExist(path) {
   288  		confirmAndRemoveDB(path, "full node ancient database")
   289  	} else {
   290  		log.Info("Full node ancient database missing", "path", path)
   291  	}
   292  	// Remove the light node database
   293  	path = stack.ResolvePath("lightchaindata")
   294  	if common.FileExist(path) {
   295  		confirmAndRemoveDB(path, "light node database")
   296  	} else {
   297  		log.Info("Light node database missing", "path", path)
   298  	}
   299  	return nil
   300  }
   301  
   302  // confirmAndRemoveDB prompts the user for a last confirmation and removes the
   303  // folder if accepted.
   304  func confirmAndRemoveDB(database string, kind string) {
   305  	confirm, err := prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database))
   306  	switch {
   307  	case err != nil:
   308  		utils.Fatalf("%v", err)
   309  	case !confirm:
   310  		log.Info("Database deletion skipped", "path", database)
   311  	default:
   312  		start := time.Now()
   313  		filepath.Walk(database, func(path string, info os.FileInfo, err error) error {
   314  			// If we're at the top level folder, recurse into
   315  			if path == database {
   316  				return nil
   317  			}
   318  			// Delete all the files, but not subfolders
   319  			if !info.IsDir() {
   320  				os.Remove(path)
   321  				return nil
   322  			}
   323  			return filepath.SkipDir
   324  		})
   325  		log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start)))
   326  	}
   327  }
   328  
   329  func inspect(ctx *cli.Context) error {
   330  	var (
   331  		prefix []byte
   332  		start  []byte
   333  	)
   334  	if ctx.NArg() > 2 {
   335  		return fmt.Errorf("Max 2 arguments: %v", ctx.Command.ArgsUsage)
   336  	}
   337  	if ctx.NArg() >= 1 {
   338  		if d, err := hexutil.Decode(ctx.Args().Get(0)); err != nil {
   339  			return fmt.Errorf("failed to hex-decode 'prefix': %v", err)
   340  		} else {
   341  			prefix = d
   342  		}
   343  	}
   344  	if ctx.NArg() >= 2 {
   345  		if d, err := hexutil.Decode(ctx.Args().Get(1)); err != nil {
   346  			return fmt.Errorf("failed to hex-decode 'start': %v", err)
   347  		} else {
   348  			start = d
   349  		}
   350  	}
   351  	stack, _ := makeConfigNode(ctx)
   352  	defer stack.Close()
   353  
   354  	db := utils.MakeChainDatabase(ctx, stack, true)
   355  	defer db.Close()
   356  
   357  	return rawdb.InspectDatabase(db, prefix, start)
   358  }
   359  
   360  func showLeveldbStats(db ethdb.Stater) {
   361  	if stats, err := db.Stat("leveldb.stats"); err != nil {
   362  		log.Warn("Failed to read database stats", "error", err)
   363  	} else {
   364  		fmt.Println(stats)
   365  	}
   366  	if ioStats, err := db.Stat("leveldb.iostats"); err != nil {
   367  		log.Warn("Failed to read database iostats", "error", err)
   368  	} else {
   369  		fmt.Println(ioStats)
   370  	}
   371  }
   372  
   373  func dbStats(ctx *cli.Context) error {
   374  	stack, _ := makeConfigNode(ctx)
   375  	defer stack.Close()
   376  
   377  	db := utils.MakeChainDatabase(ctx, stack, true)
   378  	defer db.Close()
   379  
   380  	showLeveldbStats(db)
   381  	return nil
   382  }
   383  
   384  func dbCompact(ctx *cli.Context) error {
   385  	stack, _ := makeConfigNode(ctx)
   386  	defer stack.Close()
   387  
   388  	db := utils.MakeChainDatabase(ctx, stack, false)
   389  	defer db.Close()
   390  
   391  	log.Info("Stats before compaction")
   392  	showLeveldbStats(db)
   393  
   394  	log.Info("Triggering compaction")
   395  	if err := db.Compact(nil, nil); err != nil {
   396  		log.Info("Compact err", "error", err)
   397  		return err
   398  	}
   399  	log.Info("Stats after compaction")
   400  	showLeveldbStats(db)
   401  	return nil
   402  }
   403  
   404  // dbGet shows the value of a given database key
   405  func dbGet(ctx *cli.Context) error {
   406  	if ctx.NArg() != 1 {
   407  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   408  	}
   409  	stack, _ := makeConfigNode(ctx)
   410  	defer stack.Close()
   411  
   412  	db := utils.MakeChainDatabase(ctx, stack, true)
   413  	defer db.Close()
   414  
   415  	key, err := parseHexOrString(ctx.Args().Get(0))
   416  	if err != nil {
   417  		log.Info("Could not decode the key", "error", err)
   418  		return err
   419  	}
   420  
   421  	data, err := db.Get(key)
   422  	if err != nil {
   423  		log.Info("Get operation failed", "key", fmt.Sprintf("0x%#x", key), "error", err)
   424  		return err
   425  	}
   426  	fmt.Printf("key %#x: %#x\n", key, data)
   427  	return nil
   428  }
   429  
   430  // dbDelete deletes a key from the database
   431  func dbDelete(ctx *cli.Context) error {
   432  	if ctx.NArg() != 1 {
   433  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   434  	}
   435  	stack, _ := makeConfigNode(ctx)
   436  	defer stack.Close()
   437  
   438  	db := utils.MakeChainDatabase(ctx, stack, false)
   439  	defer db.Close()
   440  
   441  	key, err := parseHexOrString(ctx.Args().Get(0))
   442  	if err != nil {
   443  		log.Info("Could not decode the key", "error", err)
   444  		return err
   445  	}
   446  	data, err := db.Get(key)
   447  	if err == nil {
   448  		fmt.Printf("Previous value: %#x\n", data)
   449  	}
   450  	if err = db.Delete(key); err != nil {
   451  		log.Info("Delete operation returned an error", "key", fmt.Sprintf("0x%#x", key), "error", err)
   452  		return err
   453  	}
   454  	return nil
   455  }
   456  
   457  // dbPut overwrite a value in the database
   458  func dbPut(ctx *cli.Context) error {
   459  	if ctx.NArg() != 2 {
   460  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   461  	}
   462  	stack, _ := makeConfigNode(ctx)
   463  	defer stack.Close()
   464  
   465  	db := utils.MakeChainDatabase(ctx, stack, false)
   466  	defer db.Close()
   467  
   468  	var (
   469  		key   []byte
   470  		value []byte
   471  		data  []byte
   472  		err   error
   473  	)
   474  	key, err = parseHexOrString(ctx.Args().Get(0))
   475  	if err != nil {
   476  		log.Info("Could not decode the key", "error", err)
   477  		return err
   478  	}
   479  	value, err = hexutil.Decode(ctx.Args().Get(1))
   480  	if err != nil {
   481  		log.Info("Could not decode the value", "error", err)
   482  		return err
   483  	}
   484  	data, err = db.Get(key)
   485  	if err == nil {
   486  		fmt.Printf("Previous value: %#x\n", data)
   487  	}
   488  	return db.Put(key, value)
   489  }
   490  
   491  // dbDumpTrie shows the key-value slots of a given storage trie
   492  func dbDumpTrie(ctx *cli.Context) error {
   493  	if ctx.NArg() < 1 {
   494  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   495  	}
   496  	stack, _ := makeConfigNode(ctx)
   497  	defer stack.Close()
   498  
   499  	db := utils.MakeChainDatabase(ctx, stack, true)
   500  	defer db.Close()
   501  	var (
   502  		root  []byte
   503  		start []byte
   504  		max   = int64(-1)
   505  		err   error
   506  	)
   507  	if root, err = hexutil.Decode(ctx.Args().Get(0)); err != nil {
   508  		log.Info("Could not decode the root", "error", err)
   509  		return err
   510  	}
   511  	stRoot := common.BytesToHash(root)
   512  	if ctx.NArg() >= 2 {
   513  		if start, err = hexutil.Decode(ctx.Args().Get(1)); err != nil {
   514  			log.Info("Could not decode the seek position", "error", err)
   515  			return err
   516  		}
   517  	}
   518  	if ctx.NArg() >= 3 {
   519  		if max, err = strconv.ParseInt(ctx.Args().Get(2), 10, 64); err != nil {
   520  			log.Info("Could not decode the max count", "error", err)
   521  			return err
   522  		}
   523  	}
   524  	theTrie, err := trie.New(stRoot, trie.NewDatabase(db))
   525  	if err != nil {
   526  		return err
   527  	}
   528  	var count int64
   529  	it := trie.NewIterator(theTrie.NodeIterator(start))
   530  	for it.Next() {
   531  		if max > 0 && count == max {
   532  			fmt.Printf("Exiting after %d values\n", count)
   533  			break
   534  		}
   535  		fmt.Printf("  %d. key %#x: %#x\n", count, it.Key, it.Value)
   536  		count++
   537  	}
   538  	return it.Err
   539  }
   540  
   541  func freezerInspect(ctx *cli.Context) error {
   542  	var (
   543  		start, end    int64
   544  		disableSnappy bool
   545  		err           error
   546  	)
   547  	if ctx.NArg() < 3 {
   548  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   549  	}
   550  	kind := ctx.Args().Get(0)
   551  	if noSnap, ok := rawdb.FreezerNoSnappy[kind]; !ok {
   552  		var options []string
   553  		for opt := range rawdb.FreezerNoSnappy {
   554  			options = append(options, opt)
   555  		}
   556  		sort.Strings(options)
   557  		return fmt.Errorf("Could read freezer-type '%v'. Available options: %v", kind, options)
   558  	} else {
   559  		disableSnappy = noSnap
   560  	}
   561  	if start, err = strconv.ParseInt(ctx.Args().Get(1), 10, 64); err != nil {
   562  		log.Info("Could read start-param", "error", err)
   563  		return err
   564  	}
   565  	if end, err = strconv.ParseInt(ctx.Args().Get(2), 10, 64); err != nil {
   566  		log.Info("Could read count param", "error", err)
   567  		return err
   568  	}
   569  	stack, _ := makeConfigNode(ctx)
   570  	defer stack.Close()
   571  	path := filepath.Join(stack.ResolvePath("chaindata"), "ancient")
   572  	log.Info("Opening freezer", "location", path, "name", kind)
   573  	if f, err := rawdb.NewFreezerTable(path, kind, disableSnappy); err != nil {
   574  		return err
   575  	} else {
   576  		f.DumpIndex(start, end)
   577  	}
   578  	return nil
   579  }
   580  
   581  // ParseHexOrString tries to hexdecode b, but if the prefix is missing, it instead just returns the raw bytes
   582  func parseHexOrString(str string) ([]byte, error) {
   583  	b, err := hexutil.Decode(str)
   584  	if errors.Is(err, hexutil.ErrMissingPrefix) {
   585  		return []byte(str), nil
   586  	}
   587  	return b, err
   588  }
   589  
   590  func importLDBdata(ctx *cli.Context) error {
   591  	start := 0
   592  	switch ctx.NArg() {
   593  	case 1:
   594  		break
   595  	case 2:
   596  		s, err := strconv.Atoi(ctx.Args().Get(1))
   597  		if err != nil {
   598  			return fmt.Errorf("second arg must be an integer: %v", err)
   599  		}
   600  		start = s
   601  	default:
   602  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   603  	}
   604  	var (
   605  		fName     = ctx.Args().Get(0)
   606  		stack, _  = makeConfigNode(ctx)
   607  		interrupt = make(chan os.Signal, 1)
   608  		stop      = make(chan struct{})
   609  	)
   610  	defer stack.Close()
   611  	signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
   612  	defer signal.Stop(interrupt)
   613  	defer close(interrupt)
   614  	go func() {
   615  		if _, ok := <-interrupt; ok {
   616  			log.Info("Interrupted during ldb import, stopping at next batch")
   617  		}
   618  		close(stop)
   619  	}()
   620  	db := utils.MakeChainDatabase(ctx, stack, false)
   621  	return utils.ImportLDBData(db, fName, int64(start), stop)
   622  }
   623  
   624  type preimageIterator struct {
   625  	iter ethdb.Iterator
   626  }
   627  
   628  func (iter *preimageIterator) Next() (byte, []byte, []byte, bool) {
   629  	for iter.iter.Next() {
   630  		key := iter.iter.Key()
   631  		if bytes.HasPrefix(key, rawdb.PreimagePrefix) && len(key) == (len(rawdb.PreimagePrefix)+common.HashLength) {
   632  			return utils.OpBatchAdd, key, iter.iter.Value(), true
   633  		}
   634  	}
   635  	return 0, nil, nil, false
   636  }
   637  
   638  func (iter *preimageIterator) Release() {
   639  	iter.iter.Release()
   640  }
   641  
   642  type snapshotIterator struct {
   643  	init    bool
   644  	account ethdb.Iterator
   645  	storage ethdb.Iterator
   646  }
   647  
   648  func (iter *snapshotIterator) Next() (byte, []byte, []byte, bool) {
   649  	if !iter.init {
   650  		iter.init = true
   651  		return utils.OpBatchDel, rawdb.SnapshotRootKey, nil, true
   652  	}
   653  	for iter.account.Next() {
   654  		key := iter.account.Key()
   655  		if bytes.HasPrefix(key, rawdb.SnapshotAccountPrefix) && len(key) == (len(rawdb.SnapshotAccountPrefix)+common.HashLength) {
   656  			return utils.OpBatchAdd, key, iter.account.Value(), true
   657  		}
   658  	}
   659  	for iter.storage.Next() {
   660  		key := iter.storage.Key()
   661  		if bytes.HasPrefix(key, rawdb.SnapshotStoragePrefix) && len(key) == (len(rawdb.SnapshotStoragePrefix)+2*common.HashLength) {
   662  			return utils.OpBatchAdd, key, iter.storage.Value(), true
   663  		}
   664  	}
   665  	return 0, nil, nil, false
   666  }
   667  
   668  func (iter *snapshotIterator) Release() {
   669  	iter.account.Release()
   670  	iter.storage.Release()
   671  }
   672  
   673  // chainExporters defines the export scheme for all exportable chain data.
   674  var chainExporters = map[string]func(db ethdb.Database) utils.ChainDataIterator{
   675  	"preimage": func(db ethdb.Database) utils.ChainDataIterator {
   676  		iter := db.NewIterator(rawdb.PreimagePrefix, nil)
   677  		return &preimageIterator{iter: iter}
   678  	},
   679  	"snapshot": func(db ethdb.Database) utils.ChainDataIterator {
   680  		account := db.NewIterator(rawdb.SnapshotAccountPrefix, nil)
   681  		storage := db.NewIterator(rawdb.SnapshotStoragePrefix, nil)
   682  		return &snapshotIterator{account: account, storage: storage}
   683  	},
   684  }
   685  
   686  func exportChaindata(ctx *cli.Context) error {
   687  	if ctx.NArg() < 2 {
   688  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   689  	}
   690  	// Parse the required chain data type, make sure it's supported.
   691  	kind := ctx.Args().Get(0)
   692  	kind = strings.ToLower(strings.Trim(kind, " "))
   693  	exporter, ok := chainExporters[kind]
   694  	if !ok {
   695  		var kinds []string
   696  		for kind := range chainExporters {
   697  			kinds = append(kinds, kind)
   698  		}
   699  		return fmt.Errorf("invalid data type %s, supported types: %s", kind, strings.Join(kinds, ", "))
   700  	}
   701  	var (
   702  		stack, _  = makeConfigNode(ctx)
   703  		interrupt = make(chan os.Signal, 1)
   704  		stop      = make(chan struct{})
   705  	)
   706  	defer stack.Close()
   707  	signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
   708  	defer signal.Stop(interrupt)
   709  	defer close(interrupt)
   710  	go func() {
   711  		if _, ok := <-interrupt; ok {
   712  			log.Info("Interrupted during db export, stopping at next batch")
   713  		}
   714  		close(stop)
   715  	}()
   716  	db := utils.MakeChainDatabase(ctx, stack, true)
   717  	return utils.ExportChaindata(ctx.Args().Get(1), kind, exporter(db), stop)
   718  }