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