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