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