github.com/gilgames000/kcc-geth@v1.0.6/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  	"time"
    24  
    25  	"github.com/ethereum/go-ethereum/cmd/utils"
    26  	"github.com/ethereum/go-ethereum/common"
    27  	"github.com/ethereum/go-ethereum/common/hexutil"
    28  	"github.com/ethereum/go-ethereum/console/prompt"
    29  	"github.com/ethereum/go-ethereum/core/rawdb"
    30  	"github.com/ethereum/go-ethereum/ethdb"
    31  	"github.com/ethereum/go-ethereum/ethdb/leveldb"
    32  	"github.com/ethereum/go-ethereum/log"
    33  	"github.com/syndtr/goleveldb/leveldb/opt"
    34  	"gopkg.in/urfave/cli.v1"
    35  )
    36  
    37  var (
    38  	removedbCommand = cli.Command{
    39  		Action:    utils.MigrateFlags(removeDB),
    40  		Name:      "removedb",
    41  		Usage:     "Remove blockchain and state databases",
    42  		ArgsUsage: "",
    43  		Flags: []cli.Flag{
    44  			utils.DataDirFlag,
    45  		},
    46  		Category: "DATABASE COMMANDS",
    47  		Description: `
    48  Remove blockchain and state databases`,
    49  	}
    50  	dbCommand = cli.Command{
    51  		Name:      "db",
    52  		Usage:     "Low level database operations",
    53  		ArgsUsage: "",
    54  		Category:  "DATABASE COMMANDS",
    55  		Subcommands: []cli.Command{
    56  			dbInspectCmd,
    57  			dbStatCmd,
    58  			dbCompactCmd,
    59  			dbGetCmd,
    60  			dbDeleteCmd,
    61  			dbPutCmd,
    62  		},
    63  	}
    64  	dbInspectCmd = cli.Command{
    65  		Action:    utils.MigrateFlags(inspect),
    66  		Name:      "inspect",
    67  		ArgsUsage: "<prefix> <start>",
    68  
    69  		Usage:       "Inspect the storage size for each type of data in the database",
    70  		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.`,
    71  	}
    72  	dbStatCmd = cli.Command{
    73  		Action: dbStats,
    74  		Name:   "stats",
    75  		Usage:  "Print leveldb statistics",
    76  	}
    77  	dbCompactCmd = cli.Command{
    78  		Action: dbCompact,
    79  		Name:   "compact",
    80  		Usage:  "Compact leveldb database. WARNING: May take a very long time",
    81  		Description: `This command performs a database compaction. 
    82  WARNING: This operation may take a very long time to finish, and may cause database
    83  corruption if it is aborted during execution'!`,
    84  	}
    85  	dbGetCmd = cli.Command{
    86  		Action:      dbGet,
    87  		Name:        "get",
    88  		Usage:       "Show the value of a database key",
    89  		ArgsUsage:   "<hex-encoded key>",
    90  		Description: "This command looks up the specified database key from the database.",
    91  	}
    92  	dbDeleteCmd = cli.Command{
    93  		Action:    dbDelete,
    94  		Name:      "delete",
    95  		Usage:     "Delete a database key (WARNING: may corrupt your database)",
    96  		ArgsUsage: "<hex-encoded key>",
    97  		Description: `This command deletes the specified database key from the database. 
    98  WARNING: This is a low-level operation which may cause database corruption!`,
    99  	}
   100  	dbPutCmd = cli.Command{
   101  		Action:    dbPut,
   102  		Name:      "put",
   103  		Usage:     "Set the value of a database key (WARNING: may corrupt your database)",
   104  		ArgsUsage: "<hex-encoded key> <hex-encoded value>",
   105  		Description: `This command sets a given database key to the given value. 
   106  WARNING: This is a low-level operation which may cause database corruption!`,
   107  	}
   108  )
   109  
   110  func removeDB(ctx *cli.Context) error {
   111  	stack, config := makeConfigNode(ctx)
   112  
   113  	// Remove the full node state database
   114  	path := stack.ResolvePath("chaindata")
   115  	if common.FileExist(path) {
   116  		confirmAndRemoveDB(path, "full node state database")
   117  	} else {
   118  		log.Info("Full node state database missing", "path", path)
   119  	}
   120  	// Remove the full node ancient database
   121  	path = config.Eth.DatabaseFreezer
   122  	switch {
   123  	case path == "":
   124  		path = filepath.Join(stack.ResolvePath("chaindata"), "ancient")
   125  	case !filepath.IsAbs(path):
   126  		path = config.Node.ResolvePath(path)
   127  	}
   128  	if common.FileExist(path) {
   129  		confirmAndRemoveDB(path, "full node ancient database")
   130  	} else {
   131  		log.Info("Full node ancient database missing", "path", path)
   132  	}
   133  	// Remove the light node database
   134  	path = stack.ResolvePath("lightchaindata")
   135  	if common.FileExist(path) {
   136  		confirmAndRemoveDB(path, "light node database")
   137  	} else {
   138  		log.Info("Light node database missing", "path", path)
   139  	}
   140  	return nil
   141  }
   142  
   143  // confirmAndRemoveDB prompts the user for a last confirmation and removes the
   144  // folder if accepted.
   145  func confirmAndRemoveDB(database string, kind string) {
   146  	confirm, err := prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database))
   147  	switch {
   148  	case err != nil:
   149  		utils.Fatalf("%v", err)
   150  	case !confirm:
   151  		log.Info("Database deletion skipped", "path", database)
   152  	default:
   153  		start := time.Now()
   154  		filepath.Walk(database, func(path string, info os.FileInfo, err error) error {
   155  			// If we're at the top level folder, recurse into
   156  			if path == database {
   157  				return nil
   158  			}
   159  			// Delete all the files, but not subfolders
   160  			if !info.IsDir() {
   161  				os.Remove(path)
   162  				return nil
   163  			}
   164  			return filepath.SkipDir
   165  		})
   166  		log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start)))
   167  	}
   168  }
   169  
   170  func inspect(ctx *cli.Context) error {
   171  	var (
   172  		prefix []byte
   173  		start  []byte
   174  	)
   175  	if ctx.NArg() > 2 {
   176  		return fmt.Errorf("Max 2 arguments: %v", ctx.Command.ArgsUsage)
   177  	}
   178  	if ctx.NArg() >= 1 {
   179  		if d, err := hexutil.Decode(ctx.Args().Get(0)); err != nil {
   180  			return fmt.Errorf("failed to hex-decode 'prefix': %v", err)
   181  		} else {
   182  			prefix = d
   183  		}
   184  	}
   185  	if ctx.NArg() >= 2 {
   186  		if d, err := hexutil.Decode(ctx.Args().Get(1)); err != nil {
   187  			return fmt.Errorf("failed to hex-decode 'start': %v", err)
   188  		} else {
   189  			start = d
   190  		}
   191  	}
   192  	stack, _ := makeConfigNode(ctx)
   193  	defer stack.Close()
   194  
   195  	_, chainDb := utils.MakeChain(ctx, stack, true)
   196  	defer chainDb.Close()
   197  
   198  	return rawdb.InspectDatabase(chainDb, prefix, start)
   199  }
   200  
   201  func showLeveldbStats(db ethdb.Stater) {
   202  	if stats, err := db.Stat("leveldb.stats"); err != nil {
   203  		log.Warn("Failed to read database stats", "error", err)
   204  	} else {
   205  		fmt.Println(stats)
   206  	}
   207  	if ioStats, err := db.Stat("leveldb.iostats"); err != nil {
   208  		log.Warn("Failed to read database iostats", "error", err)
   209  	} else {
   210  		fmt.Println(ioStats)
   211  	}
   212  }
   213  
   214  func dbStats(ctx *cli.Context) error {
   215  	stack, _ := makeConfigNode(ctx)
   216  	defer stack.Close()
   217  	path := stack.ResolvePath("chaindata")
   218  	db, err := leveldb.NewCustom(path, "", func(options *opt.Options) {
   219  		options.ReadOnly = true
   220  	})
   221  	if err != nil {
   222  		return err
   223  	}
   224  	showLeveldbStats(db)
   225  	err = db.Close()
   226  	if err != nil {
   227  		log.Info("Close err", "error", err)
   228  	}
   229  	return nil
   230  }
   231  
   232  func dbCompact(ctx *cli.Context) error {
   233  	stack, _ := makeConfigNode(ctx)
   234  	defer stack.Close()
   235  	path := stack.ResolvePath("chaindata")
   236  	cache := ctx.GlobalInt(utils.CacheFlag.Name) * ctx.GlobalInt(utils.CacheDatabaseFlag.Name) / 100
   237  	db, err := leveldb.NewCustom(path, "", func(options *opt.Options) {
   238  		options.OpenFilesCacheCapacity = utils.MakeDatabaseHandles()
   239  		options.BlockCacheCapacity = cache / 2 * opt.MiB
   240  		options.WriteBuffer = cache / 4 * opt.MiB // Two of these are used internally
   241  	})
   242  	if err != nil {
   243  		return err
   244  	}
   245  	showLeveldbStats(db)
   246  	log.Info("Triggering compaction")
   247  	err = db.Compact(nil, nil)
   248  	if err != nil {
   249  		log.Info("Compact err", "error", err)
   250  	}
   251  	showLeveldbStats(db)
   252  	log.Info("Closing db")
   253  	err = db.Close()
   254  	if err != nil {
   255  		log.Info("Close err", "error", err)
   256  	}
   257  	log.Info("Exiting")
   258  	return err
   259  }
   260  
   261  // dbGet shows the value of a given database key
   262  func dbGet(ctx *cli.Context) error {
   263  	if ctx.NArg() != 1 {
   264  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   265  	}
   266  	stack, _ := makeConfigNode(ctx)
   267  	defer stack.Close()
   268  	path := stack.ResolvePath("chaindata")
   269  	db, err := leveldb.NewCustom(path, "", func(options *opt.Options) {
   270  		options.ReadOnly = true
   271  	})
   272  	if err != nil {
   273  		return err
   274  	}
   275  	defer db.Close()
   276  	key, err := hexutil.Decode(ctx.Args().Get(0))
   277  	if err != nil {
   278  		log.Info("Could not decode the key", "error", err)
   279  		return err
   280  	}
   281  	data, err := db.Get(key)
   282  	if err != nil {
   283  		log.Info("Get operation failed", "error", err)
   284  		return err
   285  	}
   286  	fmt.Printf("key %#x:\n\t%#x\n", key, data)
   287  	return nil
   288  }
   289  
   290  // dbDelete deletes a key from the database
   291  func dbDelete(ctx *cli.Context) error {
   292  	if ctx.NArg() != 1 {
   293  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   294  	}
   295  	stack, _ := makeConfigNode(ctx)
   296  	defer stack.Close()
   297  	db := utils.MakeChainDatabase(ctx, stack)
   298  	defer db.Close()
   299  	key, err := hexutil.Decode(ctx.Args().Get(0))
   300  	if err != nil {
   301  		log.Info("Could not decode the key", "error", err)
   302  		return err
   303  	}
   304  	if err = db.Delete(key); err != nil {
   305  		log.Info("Delete operation returned an error", "error", err)
   306  		return err
   307  	}
   308  	return nil
   309  }
   310  
   311  // dbPut overwrite a value in the database
   312  func dbPut(ctx *cli.Context) error {
   313  	if ctx.NArg() != 2 {
   314  		return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
   315  	}
   316  	stack, _ := makeConfigNode(ctx)
   317  	defer stack.Close()
   318  	db := utils.MakeChainDatabase(ctx, stack)
   319  	defer db.Close()
   320  	var (
   321  		key   []byte
   322  		value []byte
   323  		data  []byte
   324  		err   error
   325  	)
   326  	key, err = hexutil.Decode(ctx.Args().Get(0))
   327  	if err != nil {
   328  		log.Info("Could not decode the key", "error", err)
   329  		return err
   330  	}
   331  	value, err = hexutil.Decode(ctx.Args().Get(1))
   332  	if err != nil {
   333  		log.Info("Could not decode the value", "error", err)
   334  		return err
   335  	}
   336  	data, err = db.Get(key)
   337  	if err == nil {
   338  		fmt.Printf("Previous value:\n%#x\n", data)
   339  	}
   340  	return db.Put(key, value)
   341  }