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