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