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