github.com/avence12/go-ethereum@v1.5.10-0.20170320123548-1dfd65f6d047/cmd/geth/chaincmd.go (about)

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