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