github.com/fff-chain/go-fff@v0.0.0-20220726032732-1c84420b8a99/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  	"fmt"
    23  	"os"
    24  	"path/filepath"
    25  	"time"
    26  
    27  	"github.com/prometheus/tsdb/fileutil"
    28  	cli "gopkg.in/urfave/cli.v1"
    29  
    30  	"github.com/fff-chain/go-fff/cmd/utils"
    31  	"github.com/fff-chain/go-fff/common"
    32  	"github.com/fff-chain/go-fff/core/rawdb"
    33  	"github.com/fff-chain/go-fff/core/state"
    34  	"github.com/fff-chain/go-fff/core/state/pruner"
    35  	"github.com/fff-chain/go-fff/core/state/snapshot"
    36  	"github.com/fff-chain/go-fff/crypto"
    37  	"github.com/fff-chain/go-fff/ethdb"
    38  	"github.com/fff-chain/go-fff/log"
    39  	"github.com/fff-chain/go-fff/node"
    40  	"github.com/fff-chain/go-fff/rlp"
    41  	"github.com/fff-chain/go-fff/trie"
    42  )
    43  
    44  var (
    45  	// emptyRoot is the known root hash of an empty trie.
    46  	emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
    47  
    48  	// emptyCode is the known hash of the empty EVM bytecode.
    49  	emptyCode = crypto.Keccak256(nil)
    50  )
    51  
    52  var (
    53  	snapshotCommand = cli.Command{
    54  		Name:        "snapshot",
    55  		Usage:       "A set of commands based on the snapshot",
    56  		Category:    "MISCELLANEOUS COMMANDS",
    57  		Description: "",
    58  		Subcommands: []cli.Command{
    59  			{
    60  				Name:      "prune-state",
    61  				Usage:     "Prune stale ethereum state data based on the snapshot",
    62  				ArgsUsage: "<root>",
    63  				Action:    utils.MigrateFlags(pruneState),
    64  				Category:  "MISCELLANEOUS COMMANDS",
    65  				Flags: []cli.Flag{
    66  					utils.DataDirFlag,
    67  					utils.AncientFlag,
    68  					utils.RopstenFlag,
    69  					utils.RinkebyFlag,
    70  					utils.GoerliFlag,
    71  					utils.CacheTrieJournalFlag,
    72  					utils.BloomFilterSizeFlag,
    73  					utils.TriesInMemoryFlag,
    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:     "prune-block",
    92  				Usage:    "Prune block data offline",
    93  				Action:   utils.MigrateFlags(pruneBlock),
    94  				Category: "MISCELLANEOUS COMMANDS",
    95  				Flags: []cli.Flag{
    96  					utils.DataDirFlag,
    97  					utils.AncientFlag,
    98  					utils.BlockAmountReserved,
    99  					utils.TriesInMemoryFlag,
   100  					utils.CheckSnapshotWithMPT,
   101  				},
   102  				Description: `
   103  geth offline prune-block for block data in ancientdb.
   104  The amount of blocks expected for remaining after prune can be specified via block-amount-reserved in this command,
   105  will prune and only remain the specified amount of old block data in ancientdb.
   106  the brief workflow is to backup the the number of this specified amount blocks backward in original ancientdb 
   107  into new ancient_backup, then delete the original ancientdb dir and rename the ancient_backup to original one for replacement,
   108  finally assemble the statedb and new ancientDb together.
   109  The purpose of doing it is because the block data will be moved into the ancient store when it
   110  becomes old enough(exceed the Threshold 90000), the disk usage will be very large over time, and is occupied mainly by ancientDb,
   111  so it's very necessary to do block data prune, this feature will handle it.
   112  `,
   113  			},
   114  			{
   115  				Name:      "verify-state",
   116  				Usage:     "Recalculate state hash based on the snapshot for verification",
   117  				ArgsUsage: "<root>",
   118  				Action:    utils.MigrateFlags(verifyState),
   119  				Category:  "MISCELLANEOUS COMMANDS",
   120  				Flags: []cli.Flag{
   121  					utils.DataDirFlag,
   122  					utils.AncientFlag,
   123  					utils.RopstenFlag,
   124  					utils.RinkebyFlag,
   125  					utils.GoerliFlag,
   126  				},
   127  				Description: `
   128  geth snapshot verify-state <state-root>
   129  will traverse the whole accounts and storages set based on the specified
   130  snapshot and recalculate the root hash of state for verification.
   131  In other words, this command does the snapshot to trie conversion.
   132  `,
   133  			},
   134  			{
   135  				Name:      "traverse-state",
   136  				Usage:     "Traverse the state with given root hash for verification",
   137  				ArgsUsage: "<root>",
   138  				Action:    utils.MigrateFlags(traverseState),
   139  				Category:  "MISCELLANEOUS COMMANDS",
   140  				Flags: []cli.Flag{
   141  					utils.DataDirFlag,
   142  					utils.AncientFlag,
   143  					utils.RopstenFlag,
   144  					utils.RinkebyFlag,
   145  					utils.GoerliFlag,
   146  				},
   147  				Description: `
   148  geth snapshot traverse-state <state-root>
   149  will traverse the whole state from the given state root and will abort if any
   150  referenced trie node or contract code is missing. This command can be used for
   151  state integrity verification. The default checking target is the HEAD state.
   152  
   153  It's also usable without snapshot enabled.
   154  `,
   155  			},
   156  			{
   157  				Name:      "traverse-rawstate",
   158  				Usage:     "Traverse the state with given root hash for verification",
   159  				ArgsUsage: "<root>",
   160  				Action:    utils.MigrateFlags(traverseRawState),
   161  				Category:  "MISCELLANEOUS COMMANDS",
   162  				Flags: []cli.Flag{
   163  					utils.DataDirFlag,
   164  					utils.AncientFlag,
   165  					utils.RopstenFlag,
   166  					utils.RinkebyFlag,
   167  					utils.GoerliFlag,
   168  				},
   169  				Description: `
   170  geth snapshot traverse-rawstate <state-root>
   171  will traverse the whole state from the given root and will abort if any referenced
   172  trie node or contract code is missing. This command can be used for state integrity
   173  verification. The default checking target is the HEAD state. It's basically identical
   174  to traverse-state, but the check granularity is smaller. 
   175  
   176  It's also usable without snapshot enabled.
   177  `,
   178  			},
   179  		},
   180  	}
   181  )
   182  
   183  func accessDb(ctx *cli.Context, stack *node.Node) (ethdb.Database, error) {
   184  	//The layer of tries trees that keep in memory.
   185  	TriesInMemory := int(ctx.GlobalUint64(utils.TriesInMemoryFlag.Name))
   186  	chaindb := utils.MakeChainDatabase(ctx, stack, false, true)
   187  	defer chaindb.Close()
   188  
   189  	if !ctx.GlobalBool(utils.CheckSnapshotWithMPT.Name) {
   190  		return chaindb, nil
   191  	}
   192  	headBlock := rawdb.ReadHeadBlock(chaindb)
   193  	if headBlock == nil {
   194  		return nil, errors.New("failed to load head block")
   195  	}
   196  	headHeader := headBlock.Header()
   197  	//Make sure the MPT and snapshot matches before pruning, otherwise the node can not start.
   198  	snaptree, err := snapshot.New(chaindb, trie.NewDatabase(chaindb), 256, TriesInMemory, headBlock.Root(), false, false, false)
   199  	if err != nil {
   200  		log.Error("snaptree error", "err", err)
   201  		return nil, err // The relevant snapshot(s) might not exist
   202  	}
   203  
   204  	// Use the HEAD-(n-1) as the target root. The reason for picking it is:
   205  	// - in most of the normal cases, the related state is available
   206  	// - the probability of this layer being reorg is very low
   207  
   208  	// Retrieve all snapshot layers from the current HEAD.
   209  	// In theory there are n difflayers + 1 disk layer present,
   210  	// so n diff layers are expected to be returned.
   211  	layers := snaptree.Snapshots(headHeader.Root, TriesInMemory, true)
   212  	if len(layers) != TriesInMemory {
   213  		// Reject if the accumulated diff layers are less than n. It
   214  		// means in most of normal cases, there is no associated state
   215  		// with bottom-most diff layer.
   216  		log.Error("snapshot layers != TriesInMemory", "err", err)
   217  		return nil, fmt.Errorf("snapshot not old enough yet: need %d more blocks", TriesInMemory-len(layers))
   218  	}
   219  	// Use the bottom-most diff layer as the target
   220  	targetRoot := layers[len(layers)-1].Root()
   221  
   222  	// Ensure the root is really present. The weak assumption
   223  	// is the presence of root can indicate the presence of the
   224  	// entire trie.
   225  	if blob := rawdb.ReadTrieNode(chaindb, targetRoot); len(blob) == 0 {
   226  		// The special case is for clique based networks(rinkeby, goerli
   227  		// and some other private networks), it's possible that two
   228  		// consecutive blocks will have same root. In this case snapshot
   229  		// difflayer won't be created. So HEAD-(n-1) may not paired with
   230  		// head-(n-1) layer. Instead the paired layer is higher than the
   231  		// bottom-most diff layer. Try to find the bottom-most snapshot
   232  		// layer with state available.
   233  		//
   234  		// Note HEAD is ignored. Usually there is the associated
   235  		// state available, but we don't want to use the topmost state
   236  		// as the pruning target.
   237  		var found bool
   238  		for i := len(layers) - 2; i >= 1; i-- {
   239  			if blob := rawdb.ReadTrieNode(chaindb, layers[i].Root()); len(blob) != 0 {
   240  				targetRoot = layers[i].Root()
   241  				found = true
   242  				log.Info("Selecting middle-layer as the pruning target", "root", targetRoot, "depth", i)
   243  				break
   244  			}
   245  		}
   246  		if !found {
   247  			if blob := rawdb.ReadTrieNode(chaindb, snaptree.DiskRoot()); len(blob) != 0 {
   248  				targetRoot = snaptree.DiskRoot()
   249  				found = true
   250  				log.Info("Selecting disk-layer as the pruning target", "root", targetRoot)
   251  			}
   252  		}
   253  		if !found {
   254  			if len(layers) > 0 {
   255  				log.Error("no snapshot paired state")
   256  				return nil, errors.New("no snapshot paired state")
   257  			}
   258  			return nil, fmt.Errorf("associated state[%x] is not present", targetRoot)
   259  		}
   260  	} else {
   261  		if len(layers) > 0 {
   262  			log.Info("Selecting bottom-most difflayer as the pruning target", "root", targetRoot, "height", headHeader.Number.Uint64()-uint64(len(layers)-1))
   263  		} else {
   264  			log.Info("Selecting user-specified state as the pruning target", "root", targetRoot)
   265  		}
   266  	}
   267  	return chaindb, nil
   268  }
   269  
   270  func pruneBlock(ctx *cli.Context) error {
   271  	stack, config := makeConfigNode(ctx)
   272  	defer stack.Close()
   273  	blockAmountReserved := ctx.GlobalUint64(utils.BlockAmountReserved.Name)
   274  	chaindb, err := accessDb(ctx, stack)
   275  	if err != nil {
   276  		return err
   277  	}
   278  	var newAncientPath string
   279  	oldAncientPath := ctx.GlobalString(utils.AncientFlag.Name)
   280  	if !filepath.IsAbs(oldAncientPath) {
   281  		oldAncientPath = stack.ResolvePath(oldAncientPath)
   282  	}
   283  
   284  	path, _ := filepath.Split(oldAncientPath)
   285  	if path == "" {
   286  		return errors.New("prune failed, did not specify the AncientPath")
   287  	}
   288  	newAncientPath = filepath.Join(path, "ancient_back")
   289  
   290  	blockpruner := pruner.NewBlockPruner(chaindb, stack, oldAncientPath, newAncientPath, blockAmountReserved)
   291  
   292  	lock, exist, err := fileutil.Flock(filepath.Join(oldAncientPath, "PRUNEFLOCK"))
   293  	if err != nil {
   294  		log.Error("file lock error", "err", err)
   295  		return err
   296  	}
   297  	if exist {
   298  		defer lock.Release()
   299  		log.Info("file lock existed, waiting for prune recovery and continue", "err", err)
   300  		if err := blockpruner.RecoverInterruption("chaindata", config.Eth.DatabaseCache, utils.MakeDatabaseHandles(), "", false); err != nil {
   301  			log.Error("Pruning failed", "err", err)
   302  			return err
   303  		}
   304  		log.Info("Block prune successfully")
   305  		return nil
   306  	}
   307  
   308  	if _, err := os.Stat(newAncientPath); err == nil {
   309  		// No file lock found for old ancientDB but new ancientDB exsisted, indicating the geth was interrupted
   310  		// after old ancientDB removal, this happened after backup successfully, so just rename the new ancientDB
   311  		if err := blockpruner.AncientDbReplacer(); err != nil {
   312  			log.Error("Failed to rename new ancient directory")
   313  			return err
   314  		}
   315  		log.Info("Block prune successfully")
   316  		return nil
   317  	}
   318  	name := "chaindata"
   319  	if err := blockpruner.BlockPruneBackUp(name, config.Eth.DatabaseCache, utils.MakeDatabaseHandles(), "", false, false); err != nil {
   320  		log.Error("Failed to back up block", "err", err)
   321  		return err
   322  	}
   323  
   324  	log.Info("backup block successfully")
   325  
   326  	//After backing up successfully, rename the new ancientdb name to the original one, and delete the old ancientdb
   327  	if err := blockpruner.AncientDbReplacer(); err != nil {
   328  		return err
   329  	}
   330  
   331  	lock.Release()
   332  	log.Info("Block prune successfully")
   333  	return nil
   334  }
   335  
   336  func pruneState(ctx *cli.Context) error {
   337  	stack, config := makeConfigNode(ctx)
   338  	defer stack.Close()
   339  
   340  	chaindb := utils.MakeChainDatabase(ctx, stack, false, false)
   341  	pruner, err := pruner.NewPruner(chaindb, stack.ResolvePath(""), stack.ResolvePath(config.Eth.TrieCleanCacheJournal), ctx.GlobalUint64(utils.BloomFilterSizeFlag.Name), ctx.GlobalUint64(utils.TriesInMemoryFlag.Name))
   342  	if err != nil {
   343  		log.Error("Failed to open snapshot tree", "err", err)
   344  		return err
   345  	}
   346  	if ctx.NArg() > 1 {
   347  		log.Error("Too many arguments given")
   348  		return errors.New("too many arguments")
   349  	}
   350  	var targetRoot common.Hash
   351  	if ctx.NArg() == 1 {
   352  		targetRoot, err = parseRoot(ctx.Args()[0])
   353  		if err != nil {
   354  			log.Error("Failed to resolve state root", "err", err)
   355  			return err
   356  		}
   357  	}
   358  	if err = pruner.Prune(targetRoot); err != nil {
   359  		log.Error("Failed to prune state", "err", err)
   360  		return err
   361  	}
   362  	return nil
   363  }
   364  
   365  func verifyState(ctx *cli.Context) error {
   366  	stack, _ := makeConfigNode(ctx)
   367  	defer stack.Close()
   368  
   369  	chaindb := utils.MakeChainDatabase(ctx, stack, true, false)
   370  	headBlock := rawdb.ReadHeadBlock(chaindb)
   371  	if headBlock == nil {
   372  		log.Error("Failed to load head block")
   373  		return errors.New("no head block")
   374  	}
   375  	snaptree, err := snapshot.New(chaindb, trie.NewDatabase(chaindb), 256, 128, headBlock.Root(), false, false, false)
   376  	if err != nil {
   377  		log.Error("Failed to open snapshot tree", "err", err)
   378  		return err
   379  	}
   380  	if ctx.NArg() > 1 {
   381  		log.Error("Too many arguments given")
   382  		return errors.New("too many arguments")
   383  	}
   384  	var root = headBlock.Root()
   385  	if ctx.NArg() == 1 {
   386  		root, err = parseRoot(ctx.Args()[0])
   387  		if err != nil {
   388  			log.Error("Failed to resolve state root", "err", err)
   389  			return err
   390  		}
   391  	}
   392  	if err := snaptree.Verify(root); err != nil {
   393  		log.Error("Failed to verfiy state", "root", root, "err", err)
   394  		return err
   395  	}
   396  	log.Info("Verified the state", "root", root)
   397  	return nil
   398  }
   399  
   400  // traverseState is a helper function used for pruning verification.
   401  // Basically it just iterates the trie, ensure all nodes and associated
   402  // contract codes are present.
   403  func traverseState(ctx *cli.Context) error {
   404  	stack, _ := makeConfigNode(ctx)
   405  	defer stack.Close()
   406  
   407  	chaindb := utils.MakeChainDatabase(ctx, stack, true, false)
   408  	headBlock := rawdb.ReadHeadBlock(chaindb)
   409  	if headBlock == nil {
   410  		log.Error("Failed to load head block")
   411  		return errors.New("no head block")
   412  	}
   413  	if ctx.NArg() > 1 {
   414  		log.Error("Too many arguments given")
   415  		return errors.New("too many arguments")
   416  	}
   417  	var (
   418  		root common.Hash
   419  		err  error
   420  	)
   421  	if ctx.NArg() == 1 {
   422  		root, err = parseRoot(ctx.Args()[0])
   423  		if err != nil {
   424  			log.Error("Failed to resolve state root", "err", err)
   425  			return err
   426  		}
   427  		log.Info("Start traversing the state", "root", root)
   428  	} else {
   429  		root = headBlock.Root()
   430  		log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64())
   431  	}
   432  	triedb := trie.NewDatabase(chaindb)
   433  	t, err := trie.NewSecure(root, triedb)
   434  	if err != nil {
   435  		log.Error("Failed to open trie", "root", root, "err", err)
   436  		return err
   437  	}
   438  	var (
   439  		accounts   int
   440  		slots      int
   441  		codes      int
   442  		lastReport time.Time
   443  		start      = time.Now()
   444  	)
   445  	accIter := trie.NewIterator(t.NodeIterator(nil))
   446  	for accIter.Next() {
   447  		accounts += 1
   448  		var acc state.Account
   449  		if err := rlp.DecodeBytes(accIter.Value, &acc); err != nil {
   450  			log.Error("Invalid account encountered during traversal", "err", err)
   451  			return err
   452  		}
   453  		if acc.Root != emptyRoot {
   454  			storageTrie, err := trie.NewSecure(acc.Root, triedb)
   455  			if err != nil {
   456  				log.Error("Failed to open storage trie", "root", acc.Root, "err", err)
   457  				return err
   458  			}
   459  			storageIter := trie.NewIterator(storageTrie.NodeIterator(nil))
   460  			for storageIter.Next() {
   461  				slots += 1
   462  			}
   463  			if storageIter.Err != nil {
   464  				log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Err)
   465  				return storageIter.Err
   466  			}
   467  		}
   468  		if !bytes.Equal(acc.CodeHash, emptyCode) {
   469  			code := rawdb.ReadCode(chaindb, common.BytesToHash(acc.CodeHash))
   470  			if len(code) == 0 {
   471  				log.Error("Code is missing", "hash", common.BytesToHash(acc.CodeHash))
   472  				return errors.New("missing code")
   473  			}
   474  			codes += 1
   475  		}
   476  		if time.Since(lastReport) > time.Second*8 {
   477  			log.Info("Traversing state", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
   478  			lastReport = time.Now()
   479  		}
   480  	}
   481  	if accIter.Err != nil {
   482  		log.Error("Failed to traverse state trie", "root", root, "err", accIter.Err)
   483  		return accIter.Err
   484  	}
   485  	log.Info("State is complete", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
   486  	return nil
   487  }
   488  
   489  // traverseRawState is a helper function used for pruning verification.
   490  // Basically it just iterates the trie, ensure all nodes and associated
   491  // contract codes are present. It's basically identical to traverseState
   492  // but it will check each trie node.
   493  func traverseRawState(ctx *cli.Context) error {
   494  	stack, _ := makeConfigNode(ctx)
   495  	defer stack.Close()
   496  
   497  	chaindb := utils.MakeChainDatabase(ctx, stack, true, false)
   498  	headBlock := rawdb.ReadHeadBlock(chaindb)
   499  	if headBlock == nil {
   500  		log.Error("Failed to load head block")
   501  		return errors.New("no head block")
   502  	}
   503  	if ctx.NArg() > 1 {
   504  		log.Error("Too many arguments given")
   505  		return errors.New("too many arguments")
   506  	}
   507  	var (
   508  		root common.Hash
   509  		err  error
   510  	)
   511  	if ctx.NArg() == 1 {
   512  		root, err = parseRoot(ctx.Args()[0])
   513  		if err != nil {
   514  			log.Error("Failed to resolve state root", "err", err)
   515  			return err
   516  		}
   517  		log.Info("Start traversing the state", "root", root)
   518  	} else {
   519  		root = headBlock.Root()
   520  		log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64())
   521  	}
   522  	triedb := trie.NewDatabase(chaindb)
   523  	t, err := trie.NewSecure(root, triedb)
   524  	if err != nil {
   525  		log.Error("Failed to open trie", "root", root, "err", err)
   526  		return err
   527  	}
   528  	var (
   529  		nodes      int
   530  		accounts   int
   531  		slots      int
   532  		codes      int
   533  		lastReport time.Time
   534  		start      = time.Now()
   535  	)
   536  	accIter := t.NodeIterator(nil)
   537  	for accIter.Next(true) {
   538  		nodes += 1
   539  		node := accIter.Hash()
   540  
   541  		if node != (common.Hash{}) {
   542  			// Check the present for non-empty hash node(embedded node doesn't
   543  			// have their own hash).
   544  			blob := rawdb.ReadTrieNode(chaindb, node)
   545  			if len(blob) == 0 {
   546  				log.Error("Missing trie node(account)", "hash", node)
   547  				return errors.New("missing account")
   548  			}
   549  		}
   550  		// If it's a leaf node, yes we are touching an account,
   551  		// dig into the storage trie further.
   552  		if accIter.Leaf() {
   553  			accounts += 1
   554  			var acc state.Account
   555  			if err := rlp.DecodeBytes(accIter.LeafBlob(), &acc); err != nil {
   556  				log.Error("Invalid account encountered during traversal", "err", err)
   557  				return errors.New("invalid account")
   558  			}
   559  			if acc.Root != emptyRoot {
   560  				storageTrie, err := trie.NewSecure(acc.Root, triedb)
   561  				if err != nil {
   562  					log.Error("Failed to open storage trie", "root", acc.Root, "err", err)
   563  					return errors.New("missing storage trie")
   564  				}
   565  				storageIter := storageTrie.NodeIterator(nil)
   566  				for storageIter.Next(true) {
   567  					nodes += 1
   568  					node := storageIter.Hash()
   569  
   570  					// Check the present for non-empty hash node(embedded node doesn't
   571  					// have their own hash).
   572  					if node != (common.Hash{}) {
   573  						blob := rawdb.ReadTrieNode(chaindb, node)
   574  						if len(blob) == 0 {
   575  							log.Error("Missing trie node(storage)", "hash", node)
   576  							return errors.New("missing storage")
   577  						}
   578  					}
   579  					// Bump the counter if it's leaf node.
   580  					if storageIter.Leaf() {
   581  						slots += 1
   582  					}
   583  				}
   584  				if storageIter.Error() != nil {
   585  					log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Error())
   586  					return storageIter.Error()
   587  				}
   588  			}
   589  			if !bytes.Equal(acc.CodeHash, emptyCode) {
   590  				code := rawdb.ReadCode(chaindb, common.BytesToHash(acc.CodeHash))
   591  				if len(code) == 0 {
   592  					log.Error("Code is missing", "account", common.BytesToHash(accIter.LeafKey()))
   593  					return errors.New("missing code")
   594  				}
   595  				codes += 1
   596  			}
   597  			if time.Since(lastReport) > time.Second*8 {
   598  				log.Info("Traversing state", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
   599  				lastReport = time.Now()
   600  			}
   601  		}
   602  	}
   603  	if accIter.Error() != nil {
   604  		log.Error("Failed to traverse state trie", "root", root, "err", accIter.Error())
   605  		return accIter.Error()
   606  	}
   607  	log.Info("State is complete", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
   608  	return nil
   609  }
   610  
   611  func parseRoot(input string) (common.Hash, error) {
   612  	var h common.Hash
   613  	if err := h.UnmarshalText([]byte(input)); err != nil {
   614  		return h, err
   615  	}
   616  	return h, nil
   617  }