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