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