github.com/DxChainNetwork/dxc@v0.8.1-0.20220824085222-1162e304b6e7/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  	"fmt"
    21  	"os"
    22  	"path/filepath"
    23  	"sort"
    24  	"strconv"
    25  	"time"
    26  
    27  	"github.com/DxChainNetwork/dxc/cmd/utils"
    28  	"github.com/DxChainNetwork/dxc/common"
    29  	"github.com/DxChainNetwork/dxc/common/hexutil"
    30  	"github.com/DxChainNetwork/dxc/console/prompt"
    31  	"github.com/DxChainNetwork/dxc/core/rawdb"
    32  	"github.com/DxChainNetwork/dxc/ethdb"
    33  	"github.com/DxChainNetwork/dxc/log"
    34  	"github.com/DxChainNetwork/dxc/trie"
    35  	"gopkg.in/urfave/cli.v1"
    36  )
    37  
    38  var (
    39  	removedbCommand = cli.Command{
    40  		Action:    utils.MigrateFlags(removeDB),
    41  		Name:      "removedb",
    42  		Usage:     "Remove blockchain and state databases",
    43  		ArgsUsage: "",
    44  		Flags: []cli.Flag{
    45  			utils.DataDirFlag,
    46  		},
    47  		Category: "DATABASE COMMANDS",
    48  		Description: `
    49  Remove blockchain and state databases`,
    50  	}
    51  	dbCommand = cli.Command{
    52  		Name:      "db",
    53  		Usage:     "Low level database operations",
    54  		ArgsUsage: "",
    55  		Category:  "DATABASE COMMANDS",
    56  		Subcommands: []cli.Command{
    57  			dbInspectCmd,
    58  			dbStatCmd,
    59  			dbCompactCmd,
    60  			dbGetCmd,
    61  			dbDeleteCmd,
    62  			dbPutCmd,
    63  			dbGetSlotsCmd,
    64  			dbDumpFreezerIndex,
    65  		},
    66  	}
    67  	dbInspectCmd = cli.Command{
    68  		Action:    utils.MigrateFlags(inspect),
    69  		Name:      "inspect",
    70  		ArgsUsage: "<prefix> <start>",
    71  		Flags: []cli.Flag{
    72  			utils.DataDirFlag,
    73  			utils.SyncModeFlag,
    74  			utils.MainnetFlag,
    75  			utils.TestnetFlag,
    76  		},
    77  		Usage:       "Inspect the storage size for each type of data in the database",
    78  		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.`,
    79  	}
    80  	dbStatCmd = cli.Command{
    81  		Action: utils.MigrateFlags(dbStats),
    82  		Name:   "stats",
    83  		Usage:  "Print leveldb statistics",
    84  		Flags: []cli.Flag{
    85  			utils.DataDirFlag,
    86  			utils.SyncModeFlag,
    87  			utils.MainnetFlag,
    88  			utils.TestnetFlag,
    89  		},
    90  	}
    91  	dbCompactCmd = cli.Command{
    92  		Action: utils.MigrateFlags(dbCompact),
    93  		Name:   "compact",
    94  		Usage:  "Compact leveldb database. WARNING: May take a very long time",
    95  		Flags: []cli.Flag{
    96  			utils.DataDirFlag,
    97  			utils.SyncModeFlag,
    98  			utils.MainnetFlag,
    99  			utils.TestnetFlag,
   100  			utils.CacheFlag,
   101  			utils.CacheDatabaseFlag,
   102  		},
   103  		Description: `This command performs a database compaction. 
   104  WARNING: This operation may take a very long time to finish, and may cause database
   105  corruption if it is aborted during execution'!`,
   106  	}
   107  	dbGetCmd = cli.Command{
   108  		Action:    utils.MigrateFlags(dbGet),
   109  		Name:      "get",
   110  		Usage:     "Show the value of a database key",
   111  		ArgsUsage: "<hex-encoded key>",
   112  		Flags: []cli.Flag{
   113  			utils.DataDirFlag,
   114  			utils.SyncModeFlag,
   115  			utils.MainnetFlag,
   116  			utils.TestnetFlag,
   117  		},
   118  		Description: "This command looks up the specified database key from the database.",
   119  	}
   120  	dbDeleteCmd = cli.Command{
   121  		Action:    utils.MigrateFlags(dbDelete),
   122  		Name:      "delete",
   123  		Usage:     "Delete a database key (WARNING: may corrupt your database)",
   124  		ArgsUsage: "<hex-encoded key>",
   125  		Flags: []cli.Flag{
   126  			utils.DataDirFlag,
   127  			utils.SyncModeFlag,
   128  			utils.MainnetFlag,
   129  			utils.TestnetFlag,
   130  		},
   131  		Description: `This command deletes the specified database key from the database. 
   132  WARNING: This is a low-level operation which may cause database corruption!`,
   133  	}
   134  	dbPutCmd = cli.Command{
   135  		Action:    utils.MigrateFlags(dbPut),
   136  		Name:      "put",
   137  		Usage:     "Set the value of a database key (WARNING: may corrupt your database)",
   138  		ArgsUsage: "<hex-encoded key> <hex-encoded value>",
   139  		Flags: []cli.Flag{
   140  			utils.DataDirFlag,
   141  			utils.SyncModeFlag,
   142  			utils.MainnetFlag,
   143  			utils.TestnetFlag,
   144  		},
   145  		Description: `This command sets a given database key to the given value. 
   146  WARNING: This is a low-level operation which may cause database corruption!`,
   147  	}
   148  	dbGetSlotsCmd = cli.Command{
   149  		Action:    utils.MigrateFlags(dbDumpTrie),
   150  		Name:      "dumptrie",
   151  		Usage:     "Show the storage key/values of a given storage trie",
   152  		ArgsUsage: "<hex-encoded storage trie root> <hex-encoded start (optional)> <int max elements (optional)>",
   153  		Flags: []cli.Flag{
   154  			utils.DataDirFlag,
   155  			utils.SyncModeFlag,
   156  			utils.MainnetFlag,
   157  			utils.TestnetFlag,
   158  		},
   159  		Description: "This command looks up the specified database key from the database.",
   160  	}
   161  	dbDumpFreezerIndex = cli.Command{
   162  		Action:    utils.MigrateFlags(freezerInspect),
   163  		Name:      "freezer-index",
   164  		Usage:     "Dump out the index of a given freezer type",
   165  		ArgsUsage: "<type> <start (int)> <end (int)>",
   166  		Flags: []cli.Flag{
   167  			utils.DataDirFlag,
   168  			utils.SyncModeFlag,
   169  			utils.MainnetFlag,
   170  			utils.TestnetFlag,
   171  		},
   172  		Description: "This command displays information about the freezer index.",
   173  	}
   174  )
   175  
   176  func removeDB(ctx *cli.Context) error {
   177  	stack, config := makeConfigNode(ctx)
   178  
   179  	// Remove the full node state database
   180  	path := stack.ResolvePath("chaindata")
   181  	if common.FileExist(path) {
   182  		confirmAndRemoveDB(path, "full node state database")
   183  	} else {
   184  		log.Info("Full node state database missing", "path", path)
   185  	}
   186  	// Remove the full node ancient database
   187  	path = config.Eth.DatabaseFreezer
   188  	switch {
   189  	case path == "":
   190  		path = filepath.Join(stack.ResolvePath("chaindata"), "ancient")
   191  	case !filepath.IsAbs(path):
   192  		path = config.Node.ResolvePath(path)
   193  	}
   194  	if common.FileExist(path) {
   195  		confirmAndRemoveDB(path, "full node ancient database")
   196  	} else {
   197  		log.Info("Full node ancient database missing", "path", path)
   198  	}
   199  	// Remove the light node database
   200  	path = stack.ResolvePath("lightchaindata")
   201  	if common.FileExist(path) {
   202  		confirmAndRemoveDB(path, "light node database")
   203  	} else {
   204  		log.Info("Light node database missing", "path", path)
   205  	}
   206  	return nil
   207  }
   208  
   209  // confirmAndRemoveDB prompts the user for a last confirmation and removes the
   210  // folder if accepted.
   211  func confirmAndRemoveDB(database string, kind string) {
   212  	confirm, err := prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database))
   213  	switch {
   214  	case err != nil:
   215  		utils.Fatalf("%v", err)
   216  	case !confirm:
   217  		log.Info("Database deletion skipped", "path", database)
   218  	default:
   219  		start := time.Now()
   220  		filepath.Walk(database, func(path string, info os.FileInfo, err error) error {
   221  			// If we're at the top level folder, recurse into
   222  			if path == database {
   223  				return nil
   224  			}
   225  			// Delete all the files, but not subfolders
   226  			if !info.IsDir() {
   227  				os.Remove(path)
   228  				return nil
   229  			}
   230  			return filepath.SkipDir
   231  		})
   232  		log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start)))
   233  	}
   234  }
   235  
   236  func inspect(ctx *cli.Context) error {
   237  	var (
   238  		prefix []byte
   239  		start  []byte
   240  	)
   241  	if ctx.NArg() > 2 {
   242  		return fmt.Errorf("Max 2 arguments: %v", ctx.Command.ArgsUsage)
   243  	}
   244  	if ctx.NArg() >= 1 {
   245  		if d, err := hexutil.Decode(ctx.Args().Get(0)); err != nil {
   246  			return fmt.Errorf("failed to hex-decode 'prefix': %v", err)
   247  		} else {
   248  			prefix = d
   249  		}
   250  	}
   251  	if ctx.NArg() >= 2 {
   252  		if d, err := hexutil.Decode(ctx.Args().Get(1)); err != nil {
   253  			return fmt.Errorf("failed to hex-decode 'start': %v", err)
   254  		} else {
   255  			start = d
   256  		}
   257  	}
   258  	stack, _ := makeConfigNode(ctx)
   259  	defer stack.Close()
   260  
   261  	db := utils.MakeChainDatabase(ctx, stack, true)
   262  	defer db.Close()
   263  
   264  	return rawdb.InspectDatabase(db, prefix, start)
   265  }
   266  
   267  func showLeveldbStats(db ethdb.Stater) {
   268  	if stats, err := db.Stat("leveldb.stats"); err != nil {
   269  		log.Warn("Failed to read database stats", "error", err)
   270  	} else {
   271  		fmt.Println(stats)
   272  	}
   273  	if ioStats, err := db.Stat("leveldb.iostats"); err != nil {
   274  		log.Warn("Failed to read database iostats", "error", err)
   275  	} else {
   276  		fmt.Println(ioStats)
   277  	}
   278  }
   279  
   280  func dbStats(ctx *cli.Context) error {
   281  	stack, _ := makeConfigNode(ctx)
   282  	defer stack.Close()
   283  
   284  	db := utils.MakeChainDatabase(ctx, stack, true)
   285  	defer db.Close()
   286  
   287  	showLeveldbStats(db)
   288  	return nil
   289  }
   290  
   291  func dbCompact(ctx *cli.Context) error {
   292  	stack, _ := makeConfigNode(ctx)
   293  	defer stack.Close()
   294  
   295  	db := utils.MakeChainDatabase(ctx, stack, false)
   296  	defer db.Close()
   297  
   298  	log.Info("Stats before compaction")
   299  	showLeveldbStats(db)
   300  
   301  	log.Info("Triggering compaction")
   302  	if err := db.Compact(nil, nil); err != nil {
   303  		log.Info("Compact err", "error", err)
   304  		return err
   305  	}
   306  	log.Info("Stats after compaction")
   307  	showLeveldbStats(db)
   308  	return nil
   309  }
   310  
   311  // dbGet shows the value of a given database key
   312  func dbGet(ctx *cli.Context) error {
   313  	if ctx.NArg() != 1 {
   314  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   315  	}
   316  	stack, _ := makeConfigNode(ctx)
   317  	defer stack.Close()
   318  
   319  	db := utils.MakeChainDatabase(ctx, stack, true)
   320  	defer db.Close()
   321  
   322  	key, err := hexutil.Decode(ctx.Args().Get(0))
   323  	if err != nil {
   324  		log.Info("Could not decode the key", "error", err)
   325  		return err
   326  	}
   327  	data, err := db.Get(key)
   328  	if err != nil {
   329  		log.Info("Get operation failed", "error", err)
   330  		return err
   331  	}
   332  	fmt.Printf("key %#x: %#x\n", key, data)
   333  	return nil
   334  }
   335  
   336  // dbDelete deletes a key from the database
   337  func dbDelete(ctx *cli.Context) error {
   338  	if ctx.NArg() != 1 {
   339  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   340  	}
   341  	stack, _ := makeConfigNode(ctx)
   342  	defer stack.Close()
   343  
   344  	db := utils.MakeChainDatabase(ctx, stack, false)
   345  	defer db.Close()
   346  
   347  	key, err := hexutil.Decode(ctx.Args().Get(0))
   348  	if err != nil {
   349  		log.Info("Could not decode the key", "error", err)
   350  		return err
   351  	}
   352  	data, err := db.Get(key)
   353  	if err == nil {
   354  		fmt.Printf("Previous value: %#x\n", data)
   355  	}
   356  	if err = db.Delete(key); err != nil {
   357  		log.Info("Delete operation returned an error", "error", err)
   358  		return err
   359  	}
   360  	return nil
   361  }
   362  
   363  // dbPut overwrite a value in the database
   364  func dbPut(ctx *cli.Context) error {
   365  	if ctx.NArg() != 2 {
   366  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   367  	}
   368  	stack, _ := makeConfigNode(ctx)
   369  	defer stack.Close()
   370  
   371  	db := utils.MakeChainDatabase(ctx, stack, false)
   372  	defer db.Close()
   373  
   374  	var (
   375  		key   []byte
   376  		value []byte
   377  		data  []byte
   378  		err   error
   379  	)
   380  	key, err = hexutil.Decode(ctx.Args().Get(0))
   381  	if err != nil {
   382  		log.Info("Could not decode the key", "error", err)
   383  		return err
   384  	}
   385  	value, err = hexutil.Decode(ctx.Args().Get(1))
   386  	if err != nil {
   387  		log.Info("Could not decode the value", "error", err)
   388  		return err
   389  	}
   390  	data, err = db.Get(key)
   391  	if err == nil {
   392  		fmt.Printf("Previous value: %#x\n", data)
   393  	}
   394  	return db.Put(key, value)
   395  }
   396  
   397  // dbDumpTrie shows the key-value slots of a given storage trie
   398  func dbDumpTrie(ctx *cli.Context) error {
   399  	if ctx.NArg() < 1 {
   400  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   401  	}
   402  	stack, _ := makeConfigNode(ctx)
   403  	defer stack.Close()
   404  
   405  	db := utils.MakeChainDatabase(ctx, stack, true)
   406  	defer db.Close()
   407  	var (
   408  		root  []byte
   409  		start []byte
   410  		max   = int64(-1)
   411  		err   error
   412  	)
   413  	if root, err = hexutil.Decode(ctx.Args().Get(0)); err != nil {
   414  		log.Info("Could not decode the root", "error", err)
   415  		return err
   416  	}
   417  	stRoot := common.BytesToHash(root)
   418  	if ctx.NArg() >= 2 {
   419  		if start, err = hexutil.Decode(ctx.Args().Get(1)); err != nil {
   420  			log.Info("Could not decode the seek position", "error", err)
   421  			return err
   422  		}
   423  	}
   424  	if ctx.NArg() >= 3 {
   425  		if max, err = strconv.ParseInt(ctx.Args().Get(2), 10, 64); err != nil {
   426  			log.Info("Could not decode the max count", "error", err)
   427  			return err
   428  		}
   429  	}
   430  	theTrie, err := trie.New(stRoot, trie.NewDatabase(db))
   431  	if err != nil {
   432  		return err
   433  	}
   434  	var count int64
   435  	it := trie.NewIterator(theTrie.NodeIterator(start))
   436  	for it.Next() {
   437  		if max > 0 && count == max {
   438  			fmt.Printf("Exiting after %d values\n", count)
   439  			break
   440  		}
   441  		fmt.Printf("  %d. key %#x: %#x\n", count, it.Key, it.Value)
   442  		count++
   443  	}
   444  	return it.Err
   445  }
   446  
   447  func freezerInspect(ctx *cli.Context) error {
   448  	var (
   449  		start, end    int64
   450  		disableSnappy bool
   451  		err           error
   452  	)
   453  	if ctx.NArg() < 3 {
   454  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   455  	}
   456  	kind := ctx.Args().Get(0)
   457  	if noSnap, ok := rawdb.FreezerNoSnappy[kind]; !ok {
   458  		var options []string
   459  		for opt := range rawdb.FreezerNoSnappy {
   460  			options = append(options, opt)
   461  		}
   462  		sort.Strings(options)
   463  		return fmt.Errorf("Could read freezer-type '%v'. Available options: %v", kind, options)
   464  	} else {
   465  		disableSnappy = noSnap
   466  	}
   467  	if start, err = strconv.ParseInt(ctx.Args().Get(1), 10, 64); err != nil {
   468  		log.Info("Could read start-param", "error", err)
   469  		return err
   470  	}
   471  	if end, err = strconv.ParseInt(ctx.Args().Get(2), 10, 64); err != nil {
   472  		log.Info("Could read count param", "error", err)
   473  		return err
   474  	}
   475  	stack, _ := makeConfigNode(ctx)
   476  	defer stack.Close()
   477  	path := filepath.Join(stack.ResolvePath("chaindata"), "ancient")
   478  	log.Info("Opening freezer", "location", path, "name", kind)
   479  	if f, err := rawdb.NewFreezerTable(path, kind, disableSnappy); err != nil {
   480  		return err
   481  	} else {
   482  		f.DumpIndex(start, end)
   483  	}
   484  	return nil
   485  }