342 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			342 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								// Copyright 2020 The go-ethereum Authors
							 | 
						||
| 
								 | 
							
								// This file is part of go-ethereum.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// go-ethereum is free software: you can redistribute it and/or modify
							 | 
						||
| 
								 | 
							
								// it under the terms of the GNU General Public License as published by
							 | 
						||
| 
								 | 
							
								// the Free Software Foundation, either version 3 of the License, or
							 | 
						||
| 
								 | 
							
								// (at your option) any later version.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// go-ethereum is distributed in the hope that it will be useful,
							 | 
						||
| 
								 | 
							
								// but WITHOUT ANY WARRANTY; without even the implied warranty of
							 | 
						||
| 
								 | 
							
								// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
							 | 
						||
| 
								 | 
							
								// GNU General Public License for more details.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// You should have received a copy of the GNU General Public License
							 | 
						||
| 
								 | 
							
								// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								package main
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"fmt"
							 | 
						||
| 
								 | 
							
									"os"
							 | 
						||
| 
								 | 
							
									"path/filepath"
							 | 
						||
| 
								 | 
							
									"time"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									"github.com/ethereum/go-ethereum/cmd/utils"
							 | 
						||
| 
								 | 
							
									"github.com/ethereum/go-ethereum/common"
							 | 
						||
| 
								 | 
							
									"github.com/ethereum/go-ethereum/common/hexutil"
							 | 
						||
| 
								 | 
							
									"github.com/ethereum/go-ethereum/console/prompt"
							 | 
						||
| 
								 | 
							
									"github.com/ethereum/go-ethereum/core/rawdb"
							 | 
						||
| 
								 | 
							
									"github.com/ethereum/go-ethereum/ethdb"
							 | 
						||
| 
								 | 
							
									"github.com/ethereum/go-ethereum/ethdb/leveldb"
							 | 
						||
| 
								 | 
							
									"github.com/ethereum/go-ethereum/log"
							 | 
						||
| 
								 | 
							
									"github.com/syndtr/goleveldb/leveldb/opt"
							 | 
						||
| 
								 | 
							
									"gopkg.in/urfave/cli.v1"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var (
							 | 
						||
| 
								 | 
							
									removedbCommand = cli.Command{
							 | 
						||
| 
								 | 
							
										Action:    utils.MigrateFlags(removeDB),
							 | 
						||
| 
								 | 
							
										Name:      "removedb",
							 | 
						||
| 
								 | 
							
										Usage:     "Remove blockchain and state databases",
							 | 
						||
| 
								 | 
							
										ArgsUsage: "",
							 | 
						||
| 
								 | 
							
										Flags: []cli.Flag{
							 | 
						||
| 
								 | 
							
											utils.DataDirFlag,
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										Category: "DATABASE COMMANDS",
							 | 
						||
| 
								 | 
							
										Description: `
							 | 
						||
| 
								 | 
							
								Remove blockchain and state databases`,
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									dbCommand = cli.Command{
							 | 
						||
| 
								 | 
							
										Name:      "db",
							 | 
						||
| 
								 | 
							
										Usage:     "Low level database operations",
							 | 
						||
| 
								 | 
							
										ArgsUsage: "",
							 | 
						||
| 
								 | 
							
										Category:  "DATABASE COMMANDS",
							 | 
						||
| 
								 | 
							
										Subcommands: []cli.Command{
							 | 
						||
| 
								 | 
							
											dbInspectCmd,
							 | 
						||
| 
								 | 
							
											dbStatCmd,
							 | 
						||
| 
								 | 
							
											dbCompactCmd,
							 | 
						||
| 
								 | 
							
											dbGetCmd,
							 | 
						||
| 
								 | 
							
											dbDeleteCmd,
							 | 
						||
| 
								 | 
							
											dbPutCmd,
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									dbInspectCmd = cli.Command{
							 | 
						||
| 
								 | 
							
										Action:    utils.MigrateFlags(inspect),
							 | 
						||
| 
								 | 
							
										Name:      "inspect",
							 | 
						||
| 
								 | 
							
										ArgsUsage: "<prefix> <start>",
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										Usage:       "Inspect the storage size for each type of data in the database",
							 | 
						||
| 
								 | 
							
										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.`,
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									dbStatCmd = cli.Command{
							 | 
						||
| 
								 | 
							
										Action: dbStats,
							 | 
						||
| 
								 | 
							
										Name:   "stats",
							 | 
						||
| 
								 | 
							
										Usage:  "Print leveldb statistics",
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									dbCompactCmd = cli.Command{
							 | 
						||
| 
								 | 
							
										Action: dbCompact,
							 | 
						||
| 
								 | 
							
										Name:   "compact",
							 | 
						||
| 
								 | 
							
										Usage:  "Compact leveldb database. WARNING: May take a very long time",
							 | 
						||
| 
								 | 
							
										Description: `This command performs a database compaction. 
							 | 
						||
| 
								 | 
							
								WARNING: This operation may take a very long time to finish, and may cause database
							 | 
						||
| 
								 | 
							
								corruption if it is aborted during execution'!`,
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									dbGetCmd = cli.Command{
							 | 
						||
| 
								 | 
							
										Action:      dbGet,
							 | 
						||
| 
								 | 
							
										Name:        "get",
							 | 
						||
| 
								 | 
							
										Usage:       "Show the value of a database key",
							 | 
						||
| 
								 | 
							
										ArgsUsage:   "<hex-encoded key>",
							 | 
						||
| 
								 | 
							
										Description: "This command looks up the specified database key from the database.",
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									dbDeleteCmd = cli.Command{
							 | 
						||
| 
								 | 
							
										Action:    dbDelete,
							 | 
						||
| 
								 | 
							
										Name:      "delete",
							 | 
						||
| 
								 | 
							
										Usage:     "Delete a database key (WARNING: may corrupt your database)",
							 | 
						||
| 
								 | 
							
										ArgsUsage: "<hex-encoded key>",
							 | 
						||
| 
								 | 
							
										Description: `This command deletes the specified database key from the database. 
							 | 
						||
| 
								 | 
							
								WARNING: This is a low-level operation which may cause database corruption!`,
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									dbPutCmd = cli.Command{
							 | 
						||
| 
								 | 
							
										Action:    dbPut,
							 | 
						||
| 
								 | 
							
										Name:      "put",
							 | 
						||
| 
								 | 
							
										Usage:     "Set the value of a database key (WARNING: may corrupt your database)",
							 | 
						||
| 
								 | 
							
										ArgsUsage: "<hex-encoded key> <hex-encoded value>",
							 | 
						||
| 
								 | 
							
										Description: `This command sets a given database key to the given value. 
							 | 
						||
| 
								 | 
							
								WARNING: This is a low-level operation which may cause database corruption!`,
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func removeDB(ctx *cli.Context) error {
							 | 
						||
| 
								 | 
							
									stack, config := makeConfigNode(ctx)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Remove the full node state database
							 | 
						||
| 
								 | 
							
									path := stack.ResolvePath("chaindata")
							 | 
						||
| 
								 | 
							
									if common.FileExist(path) {
							 | 
						||
| 
								 | 
							
										confirmAndRemoveDB(path, "full node state database")
							 | 
						||
| 
								 | 
							
									} else {
							 | 
						||
| 
								 | 
							
										log.Info("Full node state database missing", "path", path)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									// Remove the full node ancient database
							 | 
						||
| 
								 | 
							
									path = config.Eth.DatabaseFreezer
							 | 
						||
| 
								 | 
							
									switch {
							 | 
						||
| 
								 | 
							
									case path == "":
							 | 
						||
| 
								 | 
							
										path = filepath.Join(stack.ResolvePath("chaindata"), "ancient")
							 | 
						||
| 
								 | 
							
									case !filepath.IsAbs(path):
							 | 
						||
| 
								 | 
							
										path = config.Node.ResolvePath(path)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if common.FileExist(path) {
							 | 
						||
| 
								 | 
							
										confirmAndRemoveDB(path, "full node ancient database")
							 | 
						||
| 
								 | 
							
									} else {
							 | 
						||
| 
								 | 
							
										log.Info("Full node ancient database missing", "path", path)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									// Remove the light node database
							 | 
						||
| 
								 | 
							
									path = stack.ResolvePath("lightchaindata")
							 | 
						||
| 
								 | 
							
									if common.FileExist(path) {
							 | 
						||
| 
								 | 
							
										confirmAndRemoveDB(path, "light node database")
							 | 
						||
| 
								 | 
							
									} else {
							 | 
						||
| 
								 | 
							
										log.Info("Light node database missing", "path", path)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// confirmAndRemoveDB prompts the user for a last confirmation and removes the
							 | 
						||
| 
								 | 
							
								// folder if accepted.
							 | 
						||
| 
								 | 
							
								func confirmAndRemoveDB(database string, kind string) {
							 | 
						||
| 
								 | 
							
									confirm, err := prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove %s (%s)?", kind, database))
							 | 
						||
| 
								 | 
							
									switch {
							 | 
						||
| 
								 | 
							
									case err != nil:
							 | 
						||
| 
								 | 
							
										utils.Fatalf("%v", err)
							 | 
						||
| 
								 | 
							
									case !confirm:
							 | 
						||
| 
								 | 
							
										log.Info("Database deletion skipped", "path", database)
							 | 
						||
| 
								 | 
							
									default:
							 | 
						||
| 
								 | 
							
										start := time.Now()
							 | 
						||
| 
								 | 
							
										filepath.Walk(database, func(path string, info os.FileInfo, err error) error {
							 | 
						||
| 
								 | 
							
											// If we're at the top level folder, recurse into
							 | 
						||
| 
								 | 
							
											if path == database {
							 | 
						||
| 
								 | 
							
												return nil
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											// Delete all the files, but not subfolders
							 | 
						||
| 
								 | 
							
											if !info.IsDir() {
							 | 
						||
| 
								 | 
							
												os.Remove(path)
							 | 
						||
| 
								 | 
							
												return nil
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											return filepath.SkipDir
							 | 
						||
| 
								 | 
							
										})
							 | 
						||
| 
								 | 
							
										log.Info("Database successfully deleted", "path", database, "elapsed", common.PrettyDuration(time.Since(start)))
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func inspect(ctx *cli.Context) error {
							 | 
						||
| 
								 | 
							
									var (
							 | 
						||
| 
								 | 
							
										prefix []byte
							 | 
						||
| 
								 | 
							
										start  []byte
							 | 
						||
| 
								 | 
							
									)
							 | 
						||
| 
								 | 
							
									if ctx.NArg() > 2 {
							 | 
						||
| 
								 | 
							
										return fmt.Errorf("Max 2 arguments: %v", ctx.Command.ArgsUsage)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if ctx.NArg() >= 1 {
							 | 
						||
| 
								 | 
							
										if d, err := hexutil.Decode(ctx.Args().Get(0)); err != nil {
							 | 
						||
| 
								 | 
							
											return fmt.Errorf("failed to hex-decode 'prefix': %v", err)
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											prefix = d
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if ctx.NArg() >= 2 {
							 | 
						||
| 
								 | 
							
										if d, err := hexutil.Decode(ctx.Args().Get(1)); err != nil {
							 | 
						||
| 
								 | 
							
											return fmt.Errorf("failed to hex-decode 'start': %v", err)
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											start = d
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									stack, _ := makeConfigNode(ctx)
							 | 
						||
| 
								 | 
							
									defer stack.Close()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									_, chainDb := utils.MakeChain(ctx, stack, true)
							 | 
						||
| 
								 | 
							
									defer chainDb.Close()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return rawdb.InspectDatabase(chainDb, prefix, start)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func showLeveldbStats(db ethdb.Stater) {
							 | 
						||
| 
								 | 
							
									if stats, err := db.Stat("leveldb.stats"); err != nil {
							 | 
						||
| 
								 | 
							
										log.Warn("Failed to read database stats", "error", err)
							 | 
						||
| 
								 | 
							
									} else {
							 | 
						||
| 
								 | 
							
										fmt.Println(stats)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if ioStats, err := db.Stat("leveldb.iostats"); err != nil {
							 | 
						||
| 
								 | 
							
										log.Warn("Failed to read database iostats", "error", err)
							 | 
						||
| 
								 | 
							
									} else {
							 | 
						||
| 
								 | 
							
										fmt.Println(ioStats)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func dbStats(ctx *cli.Context) error {
							 | 
						||
| 
								 | 
							
									stack, _ := makeConfigNode(ctx)
							 | 
						||
| 
								 | 
							
									defer stack.Close()
							 | 
						||
| 
								 | 
							
									path := stack.ResolvePath("chaindata")
							 | 
						||
| 
								 | 
							
									db, err := leveldb.NewCustom(path, "", func(options *opt.Options) {
							 | 
						||
| 
								 | 
							
										options.ReadOnly = true
							 | 
						||
| 
								 | 
							
									})
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									showLeveldbStats(db)
							 | 
						||
| 
								 | 
							
									err = db.Close()
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										log.Info("Close err", "error", err)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func dbCompact(ctx *cli.Context) error {
							 | 
						||
| 
								 | 
							
									stack, _ := makeConfigNode(ctx)
							 | 
						||
| 
								 | 
							
									defer stack.Close()
							 | 
						||
| 
								 | 
							
									path := stack.ResolvePath("chaindata")
							 | 
						||
| 
								 | 
							
									cache := ctx.GlobalInt(utils.CacheFlag.Name) * ctx.GlobalInt(utils.CacheDatabaseFlag.Name) / 100
							 | 
						||
| 
								 | 
							
									db, err := leveldb.NewCustom(path, "", func(options *opt.Options) {
							 | 
						||
| 
								 | 
							
										options.OpenFilesCacheCapacity = utils.MakeDatabaseHandles()
							 | 
						||
| 
								 | 
							
										options.BlockCacheCapacity = cache / 2 * opt.MiB
							 | 
						||
| 
								 | 
							
										options.WriteBuffer = cache / 4 * opt.MiB // Two of these are used internally
							 | 
						||
| 
								 | 
							
									})
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									showLeveldbStats(db)
							 | 
						||
| 
								 | 
							
									log.Info("Triggering compaction")
							 | 
						||
| 
								 | 
							
									err = db.Compact(nil, nil)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										log.Info("Compact err", "error", err)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									showLeveldbStats(db)
							 | 
						||
| 
								 | 
							
									log.Info("Closing db")
							 | 
						||
| 
								 | 
							
									err = db.Close()
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										log.Info("Close err", "error", err)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									log.Info("Exiting")
							 | 
						||
| 
								 | 
							
									return err
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// dbGet shows the value of a given database key
							 | 
						||
| 
								 | 
							
								func dbGet(ctx *cli.Context) error {
							 | 
						||
| 
								 | 
							
									if ctx.NArg() != 1 {
							 | 
						||
| 
								 | 
							
										return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									stack, _ := makeConfigNode(ctx)
							 | 
						||
| 
								 | 
							
									defer stack.Close()
							 | 
						||
| 
								 | 
							
									path := stack.ResolvePath("chaindata")
							 | 
						||
| 
								 | 
							
									db, err := leveldb.NewCustom(path, "", func(options *opt.Options) {
							 | 
						||
| 
								 | 
							
										options.ReadOnly = true
							 | 
						||
| 
								 | 
							
									})
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									defer db.Close()
							 | 
						||
| 
								 | 
							
									key, err := hexutil.Decode(ctx.Args().Get(0))
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										log.Info("Could not decode the key", "error", err)
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									data, err := db.Get(key)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										log.Info("Get operation failed", "error", err)
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									fmt.Printf("key %#x:\n\t%#x\n", key, data)
							 | 
						||
| 
								 | 
							
									return nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// dbDelete deletes a key from the database
							 | 
						||
| 
								 | 
							
								func dbDelete(ctx *cli.Context) error {
							 | 
						||
| 
								 | 
							
									if ctx.NArg() != 1 {
							 | 
						||
| 
								 | 
							
										return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									stack, _ := makeConfigNode(ctx)
							 | 
						||
| 
								 | 
							
									defer stack.Close()
							 | 
						||
| 
								 | 
							
									db := utils.MakeChainDatabase(ctx, stack)
							 | 
						||
| 
								 | 
							
									defer db.Close()
							 | 
						||
| 
								 | 
							
									key, err := hexutil.Decode(ctx.Args().Get(0))
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										log.Info("Could not decode the key", "error", err)
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if err = db.Delete(key); err != nil {
							 | 
						||
| 
								 | 
							
										log.Info("Delete operation returned an error", "error", err)
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// dbPut overwrite a value in the database
							 | 
						||
| 
								 | 
							
								func dbPut(ctx *cli.Context) error {
							 | 
						||
| 
								 | 
							
									if ctx.NArg() != 2 {
							 | 
						||
| 
								 | 
							
										return fmt.Errorf("required arguments: %v", ctx.Command.ArgsUsage)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									stack, _ := makeConfigNode(ctx)
							 | 
						||
| 
								 | 
							
									defer stack.Close()
							 | 
						||
| 
								 | 
							
									db := utils.MakeChainDatabase(ctx, stack)
							 | 
						||
| 
								 | 
							
									defer db.Close()
							 | 
						||
| 
								 | 
							
									var (
							 | 
						||
| 
								 | 
							
										key   []byte
							 | 
						||
| 
								 | 
							
										value []byte
							 | 
						||
| 
								 | 
							
										data  []byte
							 | 
						||
| 
								 | 
							
										err   error
							 | 
						||
| 
								 | 
							
									)
							 | 
						||
| 
								 | 
							
									key, err = hexutil.Decode(ctx.Args().Get(0))
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										log.Info("Could not decode the key", "error", err)
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									value, err = hexutil.Decode(ctx.Args().Get(1))
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										log.Info("Could not decode the value", "error", err)
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									data, err = db.Get(key)
							 | 
						||
| 
								 | 
							
									if err == nil {
							 | 
						||
| 
								 | 
							
										fmt.Printf("Previous value:\n%#x\n", data)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return db.Put(key, value)
							 | 
						||
| 
								 | 
							
								}
							 |