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