github.com/bearnetworkchain/go-bearnetwork@v1.10.19-0.20220604150648-d63890c2e42b/cmd/geth/snapshot.go (about)

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