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