github.com/aidoskuneen/adk-node@v0.0.0-20220315131952-2e32567cb7f4/adkgo-node/dbcmd.go (about)

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