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