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