github.com/waltonchain/waltonchain_gwtc_src@v1.1.4-0.20201225072101-8a298c95a819/cmd/gwtc/chaincmd.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of go-wtc.
     3  //
     4  // go-wtc 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-wtc 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-wtc. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package main
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"os"
    23  	"runtime"
    24  	"strconv"
    25  	"sync/atomic"
    26  	"time"
    27  
    28  	"github.com/wtc/go-wtc/cmd/utils"
    29  	"github.com/wtc/go-wtc/common"
    30  	"github.com/wtc/go-wtc/console"
    31  	"github.com/wtc/go-wtc/core"
    32  	"github.com/wtc/go-wtc/core/state"
    33  	"github.com/wtc/go-wtc/core/types"
    34  	"github.com/wtc/go-wtc/wtcdb"
    35  	"github.com/wtc/go-wtc/log"
    36  	"github.com/wtc/go-wtc/trie"
    37  	"github.com/syndtr/goleveldb/leveldb/util"
    38  	"gopkg.in/urfave/cli.v1"
    39  )
    40  
    41  var (
    42  	initCommand = cli.Command{
    43  		Action:    utils.MigrateFlags(initGenesis),
    44  		Name:      "init",
    45  		Usage:     "Bootstrap and initialize a new genesis block",
    46  		ArgsUsage: "<genesisPath>",
    47  		Flags: []cli.Flag{
    48  			utils.DataDirFlag,
    49  			utils.LightModeFlag,
    50  		},
    51  		Category: "BLOCKCHAIN COMMANDS",
    52  		Description: `
    53  The init command initializes a new genesis block and definition for the network.
    54  This is a destructive action and changes the network in which you will be
    55  participating.
    56  
    57  It expects the genesis file as argument.`,
    58  	}
    59  	importCommand = cli.Command{
    60  		Action:    utils.MigrateFlags(importChain),
    61  		Name:      "import",
    62  		Usage:     "Import a blockchain file",
    63  		ArgsUsage: "<filename> (<filename 2> ... <filename N>) ",
    64  		Flags: []cli.Flag{
    65  			utils.DataDirFlag,
    66  			utils.CacheFlag,
    67  			utils.LightModeFlag,
    68  		},
    69  		Category: "BLOCKCHAIN COMMANDS",
    70  		Description: `
    71  The import command imports blocks from an RLP-encoded form. The form can be one file
    72  with several RLP-encoded blocks, or several files can be used.
    73  
    74  If only one file is used, import error will result in failure. If several files are used, 
    75  processing will proceed even if an individual RLP-file import failure occurs.`,
    76  	}
    77  	exportCommand = cli.Command{
    78  		Action:    utils.MigrateFlags(exportChain),
    79  		Name:      "export",
    80  		Usage:     "Export blockchain into file",
    81  		ArgsUsage: "<filename> [<blockNumFirst> <blockNumLast>]",
    82  		Flags: []cli.Flag{
    83  			utils.DataDirFlag,
    84  			utils.CacheFlag,
    85  			utils.LightModeFlag,
    86  		},
    87  		Category: "BLOCKCHAIN COMMANDS",
    88  		Description: `
    89  Requires a first argument of the file to write to.
    90  Optional second and third arguments control the first and
    91  last block to write. In this mode, the file will be appended
    92  if already existing.`,
    93  	}
    94  	removedbCommand = cli.Command{
    95  		Action:    utils.MigrateFlags(removeDB),
    96  		Name:      "removedb",
    97  		Usage:     "Remove blockchain and state databases",
    98  		ArgsUsage: " ",
    99  		Flags: []cli.Flag{
   100  			utils.DataDirFlag,
   101  			utils.LightModeFlag,
   102  		},
   103  		Category: "BLOCKCHAIN COMMANDS",
   104  		Description: `
   105  Remove blockchain and state databases`,
   106  	}
   107  	dumpCommand = cli.Command{
   108  		Action:    utils.MigrateFlags(dump),
   109  		Name:      "dump",
   110  		Usage:     "Dump a specific block from storage",
   111  		ArgsUsage: "[<blockHash> | <blockNum>]...",
   112  		Flags: []cli.Flag{
   113  			utils.DataDirFlag,
   114  			utils.CacheFlag,
   115  			utils.LightModeFlag,
   116  		},
   117  		Category: "BLOCKCHAIN COMMANDS",
   118  		Description: `
   119  The arguments are interpreted as block numbers or hashes.
   120  Use "wtc dump 0" to dump the genesis block.`,
   121  	}
   122  )
   123  
   124  // initGenesis will initialise the given JSON format genesis file and writes it as
   125  // the zero'd block (i.e. genesis) or will fail hard if it can't succeed.
   126  func initGenesis(ctx *cli.Context) error {
   127  	// Make sure we have a valid genesis JSON
   128  	genesisPath := ctx.Args().First()
   129  	if len(genesisPath) == 0 {
   130  		utils.Fatalf("Must supply path to genesis JSON file")
   131  	}
   132  	file, err := os.Open(genesisPath)
   133  	if err != nil {
   134  		utils.Fatalf("Failed to read genesis file: %v", err)
   135  	}
   136  	defer file.Close()
   137  
   138  	genesis := new(core.Genesis)
   139  	if err := json.NewDecoder(file).Decode(genesis); err != nil {
   140  		utils.Fatalf("invalid genesis file: %v", err)
   141  	}
   142  	// Open an initialise both full and light databases
   143  	stack := makeFullNode(ctx)
   144  	for _, name := range []string{"chaindata", "lightchaindata"} {
   145  		chaindb, err := stack.OpenDatabase(name, 0, 0)
   146  		if err != nil {
   147  			utils.Fatalf("Failed to open database: %v", err)
   148  		}
   149  		_, hash, err := core.SetupGenesisBlock(chaindb, genesis)
   150  		if err != nil {
   151  			utils.Fatalf("Failed to write genesis block: %v", err)
   152  		}
   153  		log.Info("Genesis state update finish.", "database", name, "hash", hash)
   154  	}
   155  	return nil
   156  }
   157  
   158  func importChain(ctx *cli.Context) error {
   159  	if len(ctx.Args()) < 1 {
   160  		utils.Fatalf("This command requires an argument.")
   161  	}
   162  	stack := makeFullNode(ctx)
   163  	chain, chainDb := utils.MakeChain(ctx, stack)
   164  	defer chainDb.Close()
   165  
   166  	// Start periodically gathering memory profiles
   167  	var peakMemAlloc, peakMemSys uint64
   168  	go func() {
   169  		stats := new(runtime.MemStats)
   170  		for {
   171  			runtime.ReadMemStats(stats)
   172  			if atomic.LoadUint64(&peakMemAlloc) < stats.Alloc {
   173  				atomic.StoreUint64(&peakMemAlloc, stats.Alloc)
   174  			}
   175  			if atomic.LoadUint64(&peakMemSys) < stats.Sys {
   176  				atomic.StoreUint64(&peakMemSys, stats.Sys)
   177  			}
   178  			time.Sleep(5 * time.Second)
   179  		}
   180  	}()
   181  	// Import the chain
   182  	start := time.Now()
   183  
   184  	if len(ctx.Args()) == 1 {
   185  		if err := utils.ImportChain(chain, ctx.Args().First()); err != nil {
   186  			utils.Fatalf("Import error: %v", err)
   187  		}
   188  	} else {
   189  		for _, arg := range ctx.Args() {
   190  			if err := utils.ImportChain(chain, arg); err != nil {
   191  				log.Error("Import error", "file", arg, "err", err)
   192  			}
   193  		}
   194  	}
   195  
   196  	fmt.Printf("Import done in %v.\n\n", time.Since(start))
   197  
   198  	// Output pre-compaction stats mostly to see the import trashing
   199  	db := chainDb.(*wtcdb.LDBDatabase)
   200  
   201  	stats, err := db.LDB().GetProperty("leveldb.stats")
   202  	if err != nil {
   203  		utils.Fatalf("Failed to read database stats: %v", err)
   204  	}
   205  	fmt.Println(stats)
   206  	fmt.Printf("Trie cache misses:  %d\n", trie.CacheMisses())
   207  	fmt.Printf("Trie cache unloads: %d\n\n", trie.CacheUnloads())
   208  
   209  	// Print the memory statistics used by the importing
   210  	mem := new(runtime.MemStats)
   211  	runtime.ReadMemStats(mem)
   212  
   213  	fmt.Printf("Object memory: %.3f MB current, %.3f MB peak\n", float64(mem.Alloc)/1024/1024, float64(atomic.LoadUint64(&peakMemAlloc))/1024/1024)
   214  	fmt.Printf("System memory: %.3f MB current, %.3f MB peak\n", float64(mem.Sys)/1024/1024, float64(atomic.LoadUint64(&peakMemSys))/1024/1024)
   215  	fmt.Printf("Allocations:   %.3f million\n", float64(mem.Mallocs)/1000000)
   216  	fmt.Printf("GC pause:      %v\n\n", time.Duration(mem.PauseTotalNs))
   217  
   218  	if ctx.GlobalIsSet(utils.NoCompactionFlag.Name) {
   219  		return nil
   220  	}
   221  
   222  	// Compact the entire database to more accurately measure disk io and print the stats
   223  	start = time.Now()
   224  	fmt.Println("Compacting entire database...")
   225  	if err = db.LDB().CompactRange(util.Range{}); err != nil {
   226  		utils.Fatalf("Compaction failed: %v", err)
   227  	}
   228  	fmt.Printf("Compaction done in %v.\n\n", time.Since(start))
   229  
   230  	stats, err = db.LDB().GetProperty("leveldb.stats")
   231  	if err != nil {
   232  		utils.Fatalf("Failed to read database stats: %v", err)
   233  	}
   234  	fmt.Println(stats)
   235  
   236  	return nil
   237  }
   238  
   239  func exportChain(ctx *cli.Context) error {
   240  	if len(ctx.Args()) < 1 {
   241  		utils.Fatalf("This command requires an argument.")
   242  	}
   243  	stack := makeFullNode(ctx)
   244  	chain, _ := utils.MakeChain(ctx, stack)
   245  	start := time.Now()
   246  
   247  	var err error
   248  	fp := ctx.Args().First()
   249  	if len(ctx.Args()) < 3 {
   250  		err = utils.ExportChain(chain, fp)
   251  	} else {
   252  		// This can be improved to allow for numbers larger than 9223372036854775807
   253  		first, ferr := strconv.ParseInt(ctx.Args().Get(1), 10, 64)
   254  		last, lerr := strconv.ParseInt(ctx.Args().Get(2), 10, 64)
   255  		if ferr != nil || lerr != nil {
   256  			utils.Fatalf("Export error in parsing parameters: block number not an integer\n")
   257  		}
   258  		if first < 0 || last < 0 {
   259  			utils.Fatalf("Export error: block number must be greater than 0\n")
   260  		}
   261  		err = utils.ExportAppendChain(chain, fp, uint64(first), uint64(last))
   262  	}
   263  
   264  	if err != nil {
   265  		utils.Fatalf("Export error: %v\n", err)
   266  	}
   267  	fmt.Printf("Export done in %v", time.Since(start))
   268  	return nil
   269  }
   270  
   271  func removeDB(ctx *cli.Context) error {
   272  	stack, _ := makeConfigNode(ctx)
   273  
   274  	for _, name := range []string{"chaindata", "lightchaindata"} {
   275  		// Ensure the database exists in the first place
   276  		logger := log.New("database", name)
   277  
   278  		dbdir := stack.ResolvePath(name)
   279  		if !common.FileExist(dbdir) {
   280  			logger.Info("Database doesn't exist, skipping", "path", dbdir)
   281  			continue
   282  		}
   283  		// Confirm removal and execute
   284  		fmt.Println(dbdir)
   285  		confirm, err := console.Stdin.PromptConfirm("Remove this database?")
   286  		switch {
   287  		case err != nil:
   288  			utils.Fatalf("%v", err)
   289  		case !confirm:
   290  			logger.Warn("Database deletion aborted")
   291  		default:
   292  			start := time.Now()
   293  			os.RemoveAll(dbdir)
   294  			logger.Info("Database successfully deleted", "elapsed", common.PrettyDuration(time.Since(start)))
   295  		}
   296  	}
   297  	return nil
   298  }
   299  
   300  func dump(ctx *cli.Context) error {
   301  	stack := makeFullNode(ctx)
   302  	chain, chainDb := utils.MakeChain(ctx, stack)
   303  	for _, arg := range ctx.Args() {
   304  		var block *types.Block
   305  		if hashish(arg) {
   306  			block = chain.GetBlockByHash(common.HexToHash(arg))
   307  		} else {
   308  			num, _ := strconv.Atoi(arg)
   309  			block = chain.GetBlockByNumber(uint64(num))
   310  		}
   311  		if block == nil {
   312  			fmt.Println("{}")
   313  			utils.Fatalf("block not found")
   314  		} else {
   315  			state, err := state.New(block.Root(), state.NewDatabase(chainDb))
   316  			if err != nil {
   317  				utils.Fatalf("could not create new state: %v", err)
   318  			}
   319  			fmt.Printf("%s\n", state.Dump())
   320  		}
   321  	}
   322  	chainDb.Close()
   323  	return nil
   324  }
   325  
   326  // hashish returns true for strings that look like hashes.
   327  func hashish(x string) bool {
   328  	_, err := strconv.Atoi(x)
   329  	return err != nil
   330  }