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