github.com/phillinzzz/newBsc@v1.1.6/cmd/geth/snapshot.go (about)

     1  // Copyright 2020 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  	"bytes"
    21  	"errors"
    22  	"time"
    23  
    24  	"github.com/phillinzzz/newBsc/cmd/utils"
    25  	"github.com/phillinzzz/newBsc/common"
    26  	"github.com/phillinzzz/newBsc/core/rawdb"
    27  	"github.com/phillinzzz/newBsc/core/state"
    28  	"github.com/phillinzzz/newBsc/core/state/pruner"
    29  	"github.com/phillinzzz/newBsc/core/state/snapshot"
    30  	"github.com/phillinzzz/newBsc/crypto"
    31  	"github.com/phillinzzz/newBsc/log"
    32  	"github.com/phillinzzz/newBsc/rlp"
    33  	"github.com/phillinzzz/newBsc/trie"
    34  	cli "gopkg.in/urfave/cli.v1"
    35  )
    36  
    37  var (
    38  	// emptyRoot is the known root hash of an empty trie.
    39  	emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
    40  
    41  	// emptyCode is the known hash of the empty EVM bytecode.
    42  	emptyCode = crypto.Keccak256(nil)
    43  )
    44  
    45  var (
    46  	snapshotCommand = cli.Command{
    47  		Name:        "snapshot",
    48  		Usage:       "A set of commands based on the snapshot",
    49  		Category:    "MISCELLANEOUS COMMANDS",
    50  		Description: "",
    51  		Subcommands: []cli.Command{
    52  			{
    53  				Name:      "prune-state",
    54  				Usage:     "Prune stale ethereum state data based on the snapshot",
    55  				ArgsUsage: "<root>",
    56  				Action:    utils.MigrateFlags(pruneState),
    57  				Category:  "MISCELLANEOUS COMMANDS",
    58  				Flags: []cli.Flag{
    59  					utils.DataDirFlag,
    60  					utils.AncientFlag,
    61  					utils.RopstenFlag,
    62  					utils.RinkebyFlag,
    63  					utils.GoerliFlag,
    64  					utils.CacheTrieJournalFlag,
    65  					utils.BloomFilterSizeFlag,
    66  					utils.TriesInMemoryFlag,
    67  				},
    68  				Description: `
    69  geth snapshot prune-state <state-root>
    70  will prune historical state data with the help of the state snapshot.
    71  All trie nodes and contract codes that do not belong to the specified
    72  version state will be deleted from the database. After pruning, only
    73  two version states are available: genesis and the specific one.
    74  
    75  The default pruning target is the HEAD-127 state.
    76  
    77  WARNING: It's necessary to delete the trie clean cache after the pruning.
    78  If you specify another directory for the trie clean cache via "--cache.trie.journal"
    79  during the use of Geth, please also specify it here for correct deletion. Otherwise
    80  the trie clean cache with default directory will be deleted.
    81  `,
    82  			},
    83  			{
    84  				Name:      "verify-state",
    85  				Usage:     "Recalculate state hash based on the snapshot for verification",
    86  				ArgsUsage: "<root>",
    87  				Action:    utils.MigrateFlags(verifyState),
    88  				Category:  "MISCELLANEOUS COMMANDS",
    89  				Flags: []cli.Flag{
    90  					utils.DataDirFlag,
    91  					utils.AncientFlag,
    92  					utils.RopstenFlag,
    93  					utils.RinkebyFlag,
    94  					utils.GoerliFlag,
    95  				},
    96  				Description: `
    97  geth snapshot verify-state <state-root>
    98  will traverse the whole accounts and storages set based on the specified
    99  snapshot and recalculate the root hash of state for verification.
   100  In other words, this command does the snapshot to trie conversion.
   101  `,
   102  			},
   103  			{
   104  				Name:      "traverse-state",
   105  				Usage:     "Traverse the state with given root hash for verification",
   106  				ArgsUsage: "<root>",
   107  				Action:    utils.MigrateFlags(traverseState),
   108  				Category:  "MISCELLANEOUS COMMANDS",
   109  				Flags: []cli.Flag{
   110  					utils.DataDirFlag,
   111  					utils.AncientFlag,
   112  					utils.RopstenFlag,
   113  					utils.RinkebyFlag,
   114  					utils.GoerliFlag,
   115  				},
   116  				Description: `
   117  geth snapshot traverse-state <state-root>
   118  will traverse the whole state from the given state root and will abort if any
   119  referenced trie node or contract code is missing. This command can be used for
   120  state integrity verification. The default checking target is the HEAD state.
   121  
   122  It's also usable without snapshot enabled.
   123  `,
   124  			},
   125  			{
   126  				Name:      "traverse-rawstate",
   127  				Usage:     "Traverse the state with given root hash for verification",
   128  				ArgsUsage: "<root>",
   129  				Action:    utils.MigrateFlags(traverseRawState),
   130  				Category:  "MISCELLANEOUS COMMANDS",
   131  				Flags: []cli.Flag{
   132  					utils.DataDirFlag,
   133  					utils.AncientFlag,
   134  					utils.RopstenFlag,
   135  					utils.RinkebyFlag,
   136  					utils.GoerliFlag,
   137  				},
   138  				Description: `
   139  geth snapshot traverse-rawstate <state-root>
   140  will traverse the whole state from the given root and will abort if any referenced
   141  trie node or contract code is missing. This command can be used for state integrity
   142  verification. The default checking target is the HEAD state. It's basically identical
   143  to traverse-state, but the check granularity is smaller. 
   144  
   145  It's also usable without snapshot enabled.
   146  `,
   147  			},
   148  		},
   149  	}
   150  )
   151  
   152  func pruneState(ctx *cli.Context) error {
   153  	stack, config := makeConfigNode(ctx)
   154  	defer stack.Close()
   155  
   156  	chaindb := utils.MakeChainDatabase(ctx, stack, false)
   157  	pruner, err := pruner.NewPruner(chaindb, stack.ResolvePath(""), stack.ResolvePath(config.Eth.TrieCleanCacheJournal), ctx.GlobalUint64(utils.BloomFilterSizeFlag.Name), ctx.GlobalUint64(utils.TriesInMemoryFlag.Name))
   158  	if err != nil {
   159  		log.Error("Failed to open snapshot tree", "err", err)
   160  		return err
   161  	}
   162  	if ctx.NArg() > 1 {
   163  		log.Error("Too many arguments given")
   164  		return errors.New("too many arguments")
   165  	}
   166  	var targetRoot common.Hash
   167  	if ctx.NArg() == 1 {
   168  		targetRoot, err = parseRoot(ctx.Args()[0])
   169  		if err != nil {
   170  			log.Error("Failed to resolve state root", "err", err)
   171  			return err
   172  		}
   173  	}
   174  	if err = pruner.Prune(targetRoot); err != nil {
   175  		log.Error("Failed to prune state", "err", err)
   176  		return err
   177  	}
   178  	return nil
   179  }
   180  
   181  func verifyState(ctx *cli.Context) error {
   182  	stack, _ := makeConfigNode(ctx)
   183  	defer stack.Close()
   184  
   185  	chaindb := utils.MakeChainDatabase(ctx, stack, true)
   186  	headBlock := rawdb.ReadHeadBlock(chaindb)
   187  	if headBlock == nil {
   188  		log.Error("Failed to load head block")
   189  		return errors.New("no head block")
   190  	}
   191  	snaptree, err := snapshot.New(chaindb, trie.NewDatabase(chaindb), 256, 128, headBlock.Root(), false, false, false)
   192  	if err != nil {
   193  		log.Error("Failed to open snapshot tree", "err", err)
   194  		return err
   195  	}
   196  	if ctx.NArg() > 1 {
   197  		log.Error("Too many arguments given")
   198  		return errors.New("too many arguments")
   199  	}
   200  	var root = headBlock.Root()
   201  	if ctx.NArg() == 1 {
   202  		root, err = parseRoot(ctx.Args()[0])
   203  		if err != nil {
   204  			log.Error("Failed to resolve state root", "err", err)
   205  			return err
   206  		}
   207  	}
   208  	if err := snaptree.Verify(root); err != nil {
   209  		log.Error("Failed to verfiy state", "root", root, "err", err)
   210  		return err
   211  	}
   212  	log.Info("Verified the state", "root", root)
   213  	return nil
   214  }
   215  
   216  // traverseState is a helper function used for pruning verification.
   217  // Basically it just iterates the trie, ensure all nodes and associated
   218  // contract codes are present.
   219  func traverseState(ctx *cli.Context) error {
   220  	stack, _ := makeConfigNode(ctx)
   221  	defer stack.Close()
   222  
   223  	chaindb := utils.MakeChainDatabase(ctx, stack, true)
   224  	headBlock := rawdb.ReadHeadBlock(chaindb)
   225  	if headBlock == nil {
   226  		log.Error("Failed to load head block")
   227  		return errors.New("no head block")
   228  	}
   229  	if ctx.NArg() > 1 {
   230  		log.Error("Too many arguments given")
   231  		return errors.New("too many arguments")
   232  	}
   233  	var (
   234  		root common.Hash
   235  		err  error
   236  	)
   237  	if ctx.NArg() == 1 {
   238  		root, err = parseRoot(ctx.Args()[0])
   239  		if err != nil {
   240  			log.Error("Failed to resolve state root", "err", err)
   241  			return err
   242  		}
   243  		log.Info("Start traversing the state", "root", root)
   244  	} else {
   245  		root = headBlock.Root()
   246  		log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64())
   247  	}
   248  	triedb := trie.NewDatabase(chaindb)
   249  	t, err := trie.NewSecure(root, triedb)
   250  	if err != nil {
   251  		log.Error("Failed to open trie", "root", root, "err", err)
   252  		return err
   253  	}
   254  	var (
   255  		accounts   int
   256  		slots      int
   257  		codes      int
   258  		lastReport time.Time
   259  		start      = time.Now()
   260  	)
   261  	accIter := trie.NewIterator(t.NodeIterator(nil))
   262  	for accIter.Next() {
   263  		accounts += 1
   264  		var acc state.Account
   265  		if err := rlp.DecodeBytes(accIter.Value, &acc); err != nil {
   266  			log.Error("Invalid account encountered during traversal", "err", err)
   267  			return err
   268  		}
   269  		if acc.Root != emptyRoot {
   270  			storageTrie, err := trie.NewSecure(acc.Root, triedb)
   271  			if err != nil {
   272  				log.Error("Failed to open storage trie", "root", acc.Root, "err", err)
   273  				return err
   274  			}
   275  			storageIter := trie.NewIterator(storageTrie.NodeIterator(nil))
   276  			for storageIter.Next() {
   277  				slots += 1
   278  			}
   279  			if storageIter.Err != nil {
   280  				log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Err)
   281  				return storageIter.Err
   282  			}
   283  		}
   284  		if !bytes.Equal(acc.CodeHash, emptyCode) {
   285  			code := rawdb.ReadCode(chaindb, common.BytesToHash(acc.CodeHash))
   286  			if len(code) == 0 {
   287  				log.Error("Code is missing", "hash", common.BytesToHash(acc.CodeHash))
   288  				return errors.New("missing code")
   289  			}
   290  			codes += 1
   291  		}
   292  		if time.Since(lastReport) > time.Second*8 {
   293  			log.Info("Traversing state", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
   294  			lastReport = time.Now()
   295  		}
   296  	}
   297  	if accIter.Err != nil {
   298  		log.Error("Failed to traverse state trie", "root", root, "err", accIter.Err)
   299  		return accIter.Err
   300  	}
   301  	log.Info("State is complete", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
   302  	return nil
   303  }
   304  
   305  // traverseRawState is a helper function used for pruning verification.
   306  // Basically it just iterates the trie, ensure all nodes and associated
   307  // contract codes are present. It's basically identical to traverseState
   308  // but it will check each trie node.
   309  func traverseRawState(ctx *cli.Context) error {
   310  	stack, _ := makeConfigNode(ctx)
   311  	defer stack.Close()
   312  
   313  	chaindb := utils.MakeChainDatabase(ctx, stack, true)
   314  	headBlock := rawdb.ReadHeadBlock(chaindb)
   315  	if headBlock == nil {
   316  		log.Error("Failed to load head block")
   317  		return errors.New("no head block")
   318  	}
   319  	if ctx.NArg() > 1 {
   320  		log.Error("Too many arguments given")
   321  		return errors.New("too many arguments")
   322  	}
   323  	var (
   324  		root common.Hash
   325  		err  error
   326  	)
   327  	if ctx.NArg() == 1 {
   328  		root, err = parseRoot(ctx.Args()[0])
   329  		if err != nil {
   330  			log.Error("Failed to resolve state root", "err", err)
   331  			return err
   332  		}
   333  		log.Info("Start traversing the state", "root", root)
   334  	} else {
   335  		root = headBlock.Root()
   336  		log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64())
   337  	}
   338  	triedb := trie.NewDatabase(chaindb)
   339  	t, err := trie.NewSecure(root, triedb)
   340  	if err != nil {
   341  		log.Error("Failed to open trie", "root", root, "err", err)
   342  		return err
   343  	}
   344  	var (
   345  		nodes      int
   346  		accounts   int
   347  		slots      int
   348  		codes      int
   349  		lastReport time.Time
   350  		start      = time.Now()
   351  	)
   352  	accIter := t.NodeIterator(nil)
   353  	for accIter.Next(true) {
   354  		nodes += 1
   355  		node := accIter.Hash()
   356  
   357  		if node != (common.Hash{}) {
   358  			// Check the present for non-empty hash node(embedded node doesn't
   359  			// have their own hash).
   360  			blob := rawdb.ReadTrieNode(chaindb, node)
   361  			if len(blob) == 0 {
   362  				log.Error("Missing trie node(account)", "hash", node)
   363  				return errors.New("missing account")
   364  			}
   365  		}
   366  		// If it's a leaf node, yes we are touching an account,
   367  		// dig into the storage trie further.
   368  		if accIter.Leaf() {
   369  			accounts += 1
   370  			var acc state.Account
   371  			if err := rlp.DecodeBytes(accIter.LeafBlob(), &acc); err != nil {
   372  				log.Error("Invalid account encountered during traversal", "err", err)
   373  				return errors.New("invalid account")
   374  			}
   375  			if acc.Root != emptyRoot {
   376  				storageTrie, err := trie.NewSecure(acc.Root, triedb)
   377  				if err != nil {
   378  					log.Error("Failed to open storage trie", "root", acc.Root, "err", err)
   379  					return errors.New("missing storage trie")
   380  				}
   381  				storageIter := storageTrie.NodeIterator(nil)
   382  				for storageIter.Next(true) {
   383  					nodes += 1
   384  					node := storageIter.Hash()
   385  
   386  					// Check the present for non-empty hash node(embedded node doesn't
   387  					// have their own hash).
   388  					if node != (common.Hash{}) {
   389  						blob := rawdb.ReadTrieNode(chaindb, node)
   390  						if len(blob) == 0 {
   391  							log.Error("Missing trie node(storage)", "hash", node)
   392  							return errors.New("missing storage")
   393  						}
   394  					}
   395  					// Bump the counter if it's leaf node.
   396  					if storageIter.Leaf() {
   397  						slots += 1
   398  					}
   399  				}
   400  				if storageIter.Error() != nil {
   401  					log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Error())
   402  					return storageIter.Error()
   403  				}
   404  			}
   405  			if !bytes.Equal(acc.CodeHash, emptyCode) {
   406  				code := rawdb.ReadCode(chaindb, common.BytesToHash(acc.CodeHash))
   407  				if len(code) == 0 {
   408  					log.Error("Code is missing", "account", common.BytesToHash(accIter.LeafKey()))
   409  					return errors.New("missing code")
   410  				}
   411  				codes += 1
   412  			}
   413  			if time.Since(lastReport) > time.Second*8 {
   414  				log.Info("Traversing state", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
   415  				lastReport = time.Now()
   416  			}
   417  		}
   418  	}
   419  	if accIter.Error() != nil {
   420  		log.Error("Failed to traverse state trie", "root", root, "err", accIter.Error())
   421  		return accIter.Error()
   422  	}
   423  	log.Info("State is complete", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
   424  	return nil
   425  }
   426  
   427  func parseRoot(input string) (common.Hash, error) {
   428  	var h common.Hash
   429  	if err := h.UnmarshalText([]byte(input)); err != nil {
   430  		return h, err
   431  	}
   432  	return h, nil
   433  }