github.com/theQRL/go-zond@v0.2.1/cmd/gzond/dbcmd.go (about)

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