github.com/ethereum/go-ethereum@v1.16.1/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  	"fmt"
    24  	"os"
    25  	"slices"
    26  	"time"
    27  
    28  	"github.com/ethereum/go-ethereum/cmd/utils"
    29  	"github.com/ethereum/go-ethereum/common"
    30  	"github.com/ethereum/go-ethereum/core/rawdb"
    31  	"github.com/ethereum/go-ethereum/core/state"
    32  	"github.com/ethereum/go-ethereum/core/state/pruner"
    33  	"github.com/ethereum/go-ethereum/core/state/snapshot"
    34  	"github.com/ethereum/go-ethereum/core/types"
    35  	"github.com/ethereum/go-ethereum/crypto"
    36  	"github.com/ethereum/go-ethereum/log"
    37  	"github.com/ethereum/go-ethereum/rlp"
    38  	"github.com/ethereum/go-ethereum/trie"
    39  	"github.com/urfave/cli/v2"
    40  )
    41  
    42  var (
    43  	snapshotCommand = &cli.Command{
    44  		Name:        "snapshot",
    45  		Usage:       "A set of commands based on the snapshot",
    46  		Description: "",
    47  		Subcommands: []*cli.Command{
    48  			{
    49  				Name:      "prune-state",
    50  				Usage:     "Prune stale ethereum state data based on the snapshot",
    51  				ArgsUsage: "<root>",
    52  				Action:    pruneState,
    53  				Flags: slices.Concat([]cli.Flag{
    54  					utils.BloomFilterSizeFlag,
    55  				}, utils.NetworkFlags, utils.DatabaseFlags),
    56  				Description: `
    57  geth snapshot prune-state <state-root>
    58  will prune historical state data with the help of the state snapshot.
    59  All trie nodes and contract codes that do not belong to the specified
    60  version state will be deleted from the database. After pruning, only
    61  two version states are available: genesis and the specific one.
    62  
    63  The default pruning target is the HEAD-127 state.
    64  
    65  WARNING: it's only supported in hash mode(--state.scheme=hash)".
    66  `,
    67  			},
    68  			{
    69  				Name:      "verify-state",
    70  				Usage:     "Recalculate state hash based on the snapshot for verification",
    71  				ArgsUsage: "<root>",
    72  				Action:    verifyState,
    73  				Flags:     slices.Concat(utils.NetworkFlags, utils.DatabaseFlags),
    74  				Description: `
    75  geth snapshot verify-state <state-root>
    76  will traverse the whole accounts and storages set based on the specified
    77  snapshot and recalculate the root hash of state for verification.
    78  In other words, this command does the snapshot to trie conversion.
    79  `,
    80  			},
    81  			{
    82  				Name:      "check-dangling-storage",
    83  				Usage:     "Check that there is no 'dangling' snap storage",
    84  				ArgsUsage: "<root>",
    85  				Action:    checkDanglingStorage,
    86  				Flags:     slices.Concat(utils.NetworkFlags, utils.DatabaseFlags),
    87  				Description: `
    88  geth snapshot check-dangling-storage <state-root> traverses the snap storage
    89  data, and verifies that all snapshot storage data has a corresponding account.
    90  `,
    91  			},
    92  			{
    93  				Name:      "inspect-account",
    94  				Usage:     "Check all snapshot layers for the specific account",
    95  				ArgsUsage: "<address | hash>",
    96  				Action:    checkAccount,
    97  				Flags:     slices.Concat(utils.NetworkFlags, utils.DatabaseFlags),
    98  				Description: `
    99  geth snapshot inspect-account <address | hash> checks all snapshot layers and prints out
   100  information about the specified address.
   101  `,
   102  			},
   103  			{
   104  				Name:      "traverse-state",
   105  				Usage:     "Traverse the state with given root hash and perform quick verification",
   106  				ArgsUsage: "<root>",
   107  				Action:    traverseState,
   108  				Flags:     slices.Concat(utils.NetworkFlags, utils.DatabaseFlags),
   109  				Description: `
   110  geth snapshot traverse-state <state-root>
   111  will traverse the whole state from the given state root and will abort if any
   112  referenced trie node or contract code is missing. This command can be used for
   113  state integrity verification. The default checking target is the HEAD state.
   114  
   115  It's also usable without snapshot enabled.
   116  `,
   117  			},
   118  			{
   119  				Name:      "traverse-rawstate",
   120  				Usage:     "Traverse the state with given root hash and perform detailed verification",
   121  				ArgsUsage: "<root>",
   122  				Action:    traverseRawState,
   123  				Flags:     slices.Concat(utils.NetworkFlags, utils.DatabaseFlags),
   124  				Description: `
   125  geth snapshot traverse-rawstate <state-root>
   126  will traverse the whole state from the given root and will abort if any referenced
   127  trie node or contract code is missing. This command can be used for state integrity
   128  verification. The default checking target is the HEAD state. It's basically identical
   129  to traverse-state, but the check granularity is smaller.
   130  
   131  It's also usable without snapshot enabled.
   132  `,
   133  			},
   134  			{
   135  				Name:      "dump",
   136  				Usage:     "Dump a specific block from storage (same as 'geth dump' but using snapshots)",
   137  				ArgsUsage: "[? <blockHash> | <blockNum>]",
   138  				Action:    dumpState,
   139  				Flags: slices.Concat([]cli.Flag{
   140  					utils.ExcludeCodeFlag,
   141  					utils.ExcludeStorageFlag,
   142  					utils.StartKeyFlag,
   143  					utils.DumpLimitFlag,
   144  				}, utils.NetworkFlags, utils.DatabaseFlags),
   145  				Description: `
   146  This command is semantically equivalent to 'geth dump', but uses the snapshots
   147  as the backend data source, making this command a lot faster.
   148  
   149  The argument is interpreted as block number or hash. If none is provided, the latest
   150  block is used.
   151  `,
   152  			},
   153  			{
   154  				Action:    snapshotExportPreimages,
   155  				Name:      "export-preimages",
   156  				Usage:     "Export the preimage in snapshot enumeration order",
   157  				ArgsUsage: "<dumpfile> [<root>]",
   158  				Flags:     utils.DatabaseFlags,
   159  				Description: `
   160  The export-preimages command exports hash preimages to a flat file, in exactly
   161  the expected order for the overlay tree migration.
   162  `,
   163  			},
   164  		},
   165  	}
   166  )
   167  
   168  // Deprecation: this command should be deprecated once the hash-based
   169  // scheme is deprecated.
   170  func pruneState(ctx *cli.Context) error {
   171  	stack, _ := makeConfigNode(ctx)
   172  	defer stack.Close()
   173  
   174  	chaindb := utils.MakeChainDatabase(ctx, stack, false)
   175  	defer chaindb.Close()
   176  
   177  	if rawdb.ReadStateScheme(chaindb) != rawdb.HashScheme {
   178  		log.Crit("Offline pruning is not required for path scheme")
   179  	}
   180  	prunerconfig := pruner.Config{
   181  		Datadir:   stack.ResolvePath(""),
   182  		BloomSize: ctx.Uint64(utils.BloomFilterSizeFlag.Name),
   183  	}
   184  	pruner, err := pruner.NewPruner(chaindb, prunerconfig)
   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().First())
   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  	defer chaindb.Close()
   214  
   215  	headBlock := rawdb.ReadHeadBlock(chaindb)
   216  	if headBlock == nil {
   217  		log.Error("Failed to load head block")
   218  		return errors.New("no head block")
   219  	}
   220  	triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
   221  	defer triedb.Close()
   222  
   223  	var (
   224  		err  error
   225  		root = headBlock.Root()
   226  	)
   227  	if ctx.NArg() == 1 {
   228  		root, err = parseRoot(ctx.Args().First())
   229  		if err != nil {
   230  			log.Error("Failed to resolve state root", "err", err)
   231  			return err
   232  		}
   233  	}
   234  	if triedb.Scheme() == rawdb.PathScheme {
   235  		if err := triedb.VerifyState(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  
   241  		// TODO(rjl493456442) implement dangling checks in pathdb.
   242  		return nil
   243  	} else {
   244  		snapConfig := snapshot.Config{
   245  			CacheSize:  256,
   246  			Recovery:   false,
   247  			NoBuild:    true,
   248  			AsyncBuild: false,
   249  		}
   250  		snaptree, err := snapshot.New(snapConfig, chaindb, triedb, headBlock.Root())
   251  		if err != nil {
   252  			log.Error("Failed to open snapshot tree", "err", err)
   253  			return err
   254  		}
   255  		if err := snaptree.Verify(root); err != nil {
   256  			log.Error("Failed to verify state", "root", root, "err", err)
   257  			return err
   258  		}
   259  		log.Info("Verified the state", "root", root)
   260  		return snapshot.CheckDanglingStorage(chaindb)
   261  	}
   262  }
   263  
   264  // checkDanglingStorage iterates the snap storage data, and verifies that all
   265  // storage also has corresponding account data.
   266  func checkDanglingStorage(ctx *cli.Context) error {
   267  	stack, _ := makeConfigNode(ctx)
   268  	defer stack.Close()
   269  
   270  	db := utils.MakeChainDatabase(ctx, stack, true)
   271  	defer db.Close()
   272  	return snapshot.CheckDanglingStorage(db)
   273  }
   274  
   275  // traverseState is a helper function used for pruning verification.
   276  // Basically it just iterates the trie, ensure all nodes and associated
   277  // contract codes are present.
   278  func traverseState(ctx *cli.Context) error {
   279  	stack, _ := makeConfigNode(ctx)
   280  	defer stack.Close()
   281  
   282  	chaindb := utils.MakeChainDatabase(ctx, stack, true)
   283  	defer chaindb.Close()
   284  
   285  	triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
   286  	defer triedb.Close()
   287  
   288  	headBlock := rawdb.ReadHeadBlock(chaindb)
   289  	if headBlock == nil {
   290  		log.Error("Failed to load head block")
   291  		return errors.New("no head block")
   292  	}
   293  	if ctx.NArg() > 1 {
   294  		log.Error("Too many arguments given")
   295  		return errors.New("too many arguments")
   296  	}
   297  	var (
   298  		root common.Hash
   299  		err  error
   300  	)
   301  	if ctx.NArg() == 1 {
   302  		root, err = parseRoot(ctx.Args().First())
   303  		if err != nil {
   304  			log.Error("Failed to resolve state root", "err", err)
   305  			return err
   306  		}
   307  		log.Info("Start traversing the state", "root", root)
   308  	} else {
   309  		root = headBlock.Root()
   310  		log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64())
   311  	}
   312  	t, err := trie.NewStateTrie(trie.StateTrieID(root), triedb)
   313  	if err != nil {
   314  		log.Error("Failed to open trie", "root", root, "err", err)
   315  		return err
   316  	}
   317  	var (
   318  		accounts   int
   319  		slots      int
   320  		codes      int
   321  		lastReport time.Time
   322  		start      = time.Now()
   323  	)
   324  	acctIt, err := t.NodeIterator(nil)
   325  	if err != nil {
   326  		log.Error("Failed to open iterator", "root", root, "err", err)
   327  		return err
   328  	}
   329  	accIter := trie.NewIterator(acctIt)
   330  	for accIter.Next() {
   331  		accounts += 1
   332  		var acc types.StateAccount
   333  		if err := rlp.DecodeBytes(accIter.Value, &acc); err != nil {
   334  			log.Error("Invalid account encountered during traversal", "err", err)
   335  			return err
   336  		}
   337  		if acc.Root != types.EmptyRootHash {
   338  			id := trie.StorageTrieID(root, common.BytesToHash(accIter.Key), acc.Root)
   339  			storageTrie, err := trie.NewStateTrie(id, triedb)
   340  			if err != nil {
   341  				log.Error("Failed to open storage trie", "root", acc.Root, "err", err)
   342  				return err
   343  			}
   344  			storageIt, err := storageTrie.NodeIterator(nil)
   345  			if err != nil {
   346  				log.Error("Failed to open storage iterator", "root", acc.Root, "err", err)
   347  				return err
   348  			}
   349  			storageIter := trie.NewIterator(storageIt)
   350  			for storageIter.Next() {
   351  				slots += 1
   352  
   353  				if time.Since(lastReport) > time.Second*8 {
   354  					log.Info("Traversing state", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
   355  					lastReport = time.Now()
   356  				}
   357  			}
   358  			if storageIter.Err != nil {
   359  				log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Err)
   360  				return storageIter.Err
   361  			}
   362  		}
   363  		if !bytes.Equal(acc.CodeHash, types.EmptyCodeHash.Bytes()) {
   364  			if !rawdb.HasCode(chaindb, common.BytesToHash(acc.CodeHash)) {
   365  				log.Error("Code is missing", "hash", common.BytesToHash(acc.CodeHash))
   366  				return errors.New("missing code")
   367  			}
   368  			codes += 1
   369  		}
   370  		if time.Since(lastReport) > time.Second*8 {
   371  			log.Info("Traversing state", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
   372  			lastReport = time.Now()
   373  		}
   374  	}
   375  	if accIter.Err != nil {
   376  		log.Error("Failed to traverse state trie", "root", root, "err", accIter.Err)
   377  		return accIter.Err
   378  	}
   379  	log.Info("State is complete", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
   380  	return nil
   381  }
   382  
   383  // traverseRawState is a helper function used for pruning verification.
   384  // Basically it just iterates the trie, ensure all nodes and associated
   385  // contract codes are present. It's basically identical to traverseState
   386  // but it will check each trie node.
   387  func traverseRawState(ctx *cli.Context) error {
   388  	stack, _ := makeConfigNode(ctx)
   389  	defer stack.Close()
   390  
   391  	chaindb := utils.MakeChainDatabase(ctx, stack, true)
   392  	defer chaindb.Close()
   393  
   394  	triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
   395  	defer triedb.Close()
   396  
   397  	headBlock := rawdb.ReadHeadBlock(chaindb)
   398  	if headBlock == nil {
   399  		log.Error("Failed to load head block")
   400  		return errors.New("no head block")
   401  	}
   402  	if ctx.NArg() > 1 {
   403  		log.Error("Too many arguments given")
   404  		return errors.New("too many arguments")
   405  	}
   406  	var (
   407  		root common.Hash
   408  		err  error
   409  	)
   410  	if ctx.NArg() == 1 {
   411  		root, err = parseRoot(ctx.Args().First())
   412  		if err != nil {
   413  			log.Error("Failed to resolve state root", "err", err)
   414  			return err
   415  		}
   416  		log.Info("Start traversing the state", "root", root)
   417  	} else {
   418  		root = headBlock.Root()
   419  		log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64())
   420  	}
   421  	t, err := trie.NewStateTrie(trie.StateTrieID(root), triedb)
   422  	if err != nil {
   423  		log.Error("Failed to open trie", "root", root, "err", err)
   424  		return err
   425  	}
   426  	var (
   427  		nodes      int
   428  		accounts   int
   429  		slots      int
   430  		codes      int
   431  		lastReport time.Time
   432  		start      = time.Now()
   433  		hasher     = crypto.NewKeccakState()
   434  		got        = make([]byte, 32)
   435  	)
   436  	accIter, err := t.NodeIterator(nil)
   437  	if err != nil {
   438  		log.Error("Failed to open iterator", "root", root, "err", err)
   439  		return err
   440  	}
   441  	reader, err := triedb.NodeReader(root)
   442  	if err != nil {
   443  		log.Error("State is non-existent", "root", root)
   444  		return nil
   445  	}
   446  	for accIter.Next(true) {
   447  		nodes += 1
   448  		node := accIter.Hash()
   449  
   450  		// Check the present for non-empty hash node(embedded node doesn't
   451  		// have their own hash).
   452  		if node != (common.Hash{}) {
   453  			blob, _ := reader.Node(common.Hash{}, accIter.Path(), node)
   454  			if len(blob) == 0 {
   455  				log.Error("Missing trie node(account)", "hash", node)
   456  				return errors.New("missing account")
   457  			}
   458  			hasher.Reset()
   459  			hasher.Write(blob)
   460  			hasher.Read(got)
   461  			if !bytes.Equal(got, node.Bytes()) {
   462  				log.Error("Invalid trie node(account)", "hash", node.Hex(), "value", blob)
   463  				return errors.New("invalid account node")
   464  			}
   465  		}
   466  		// If it's a leaf node, yes we are touching an account,
   467  		// dig into the storage trie further.
   468  		if accIter.Leaf() {
   469  			accounts += 1
   470  			var acc types.StateAccount
   471  			if err := rlp.DecodeBytes(accIter.LeafBlob(), &acc); err != nil {
   472  				log.Error("Invalid account encountered during traversal", "err", err)
   473  				return errors.New("invalid account")
   474  			}
   475  			if acc.Root != types.EmptyRootHash {
   476  				id := trie.StorageTrieID(root, common.BytesToHash(accIter.LeafKey()), acc.Root)
   477  				storageTrie, err := trie.NewStateTrie(id, triedb)
   478  				if err != nil {
   479  					log.Error("Failed to open storage trie", "root", acc.Root, "err", err)
   480  					return errors.New("missing storage trie")
   481  				}
   482  				storageIter, err := storageTrie.NodeIterator(nil)
   483  				if err != nil {
   484  					log.Error("Failed to open storage iterator", "root", acc.Root, "err", err)
   485  					return err
   486  				}
   487  				for storageIter.Next(true) {
   488  					nodes += 1
   489  					node := storageIter.Hash()
   490  
   491  					// Check the presence for non-empty hash node(embedded node doesn't
   492  					// have their own hash).
   493  					if node != (common.Hash{}) {
   494  						blob, _ := reader.Node(common.BytesToHash(accIter.LeafKey()), storageIter.Path(), node)
   495  						if len(blob) == 0 {
   496  							log.Error("Missing trie node(storage)", "hash", node)
   497  							return errors.New("missing storage")
   498  						}
   499  						hasher.Reset()
   500  						hasher.Write(blob)
   501  						hasher.Read(got)
   502  						if !bytes.Equal(got, node.Bytes()) {
   503  							log.Error("Invalid trie node(storage)", "hash", node.Hex(), "value", blob)
   504  							return errors.New("invalid storage node")
   505  						}
   506  					}
   507  					// Bump the counter if it's leaf node.
   508  					if storageIter.Leaf() {
   509  						slots += 1
   510  					}
   511  					if time.Since(lastReport) > time.Second*8 {
   512  						log.Info("Traversing state", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
   513  						lastReport = time.Now()
   514  					}
   515  				}
   516  				if storageIter.Error() != nil {
   517  					log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Error())
   518  					return storageIter.Error()
   519  				}
   520  			}
   521  			if !bytes.Equal(acc.CodeHash, types.EmptyCodeHash.Bytes()) {
   522  				if !rawdb.HasCode(chaindb, common.BytesToHash(acc.CodeHash)) {
   523  					log.Error("Code is missing", "account", common.BytesToHash(accIter.LeafKey()))
   524  					return errors.New("missing code")
   525  				}
   526  				codes += 1
   527  			}
   528  			if time.Since(lastReport) > time.Second*8 {
   529  				log.Info("Traversing state", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
   530  				lastReport = time.Now()
   531  			}
   532  		}
   533  	}
   534  	if accIter.Error() != nil {
   535  		log.Error("Failed to traverse state trie", "root", root, "err", accIter.Error())
   536  		return accIter.Error()
   537  	}
   538  	log.Info("State is complete", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
   539  	return nil
   540  }
   541  
   542  func parseRoot(input string) (common.Hash, error) {
   543  	var h common.Hash
   544  	if err := h.UnmarshalText([]byte(input)); err != nil {
   545  		return h, err
   546  	}
   547  	return h, nil
   548  }
   549  
   550  func dumpState(ctx *cli.Context) error {
   551  	stack, _ := makeConfigNode(ctx)
   552  	defer stack.Close()
   553  
   554  	db := utils.MakeChainDatabase(ctx, stack, true)
   555  	defer db.Close()
   556  
   557  	conf, root, err := parseDumpConfig(ctx, db)
   558  	if err != nil {
   559  		return err
   560  	}
   561  	triedb := utils.MakeTrieDatabase(ctx, db, false, true, false)
   562  	defer triedb.Close()
   563  
   564  	snapConfig := snapshot.Config{
   565  		CacheSize:  256,
   566  		Recovery:   false,
   567  		NoBuild:    true,
   568  		AsyncBuild: false,
   569  	}
   570  	snaptree, err := snapshot.New(snapConfig, db, triedb, root)
   571  	if err != nil {
   572  		return err
   573  	}
   574  	accIt, err := snaptree.AccountIterator(root, common.BytesToHash(conf.Start))
   575  	if err != nil {
   576  		return err
   577  	}
   578  	defer accIt.Release()
   579  
   580  	log.Info("Snapshot dumping started", "root", root)
   581  	var (
   582  		start    = time.Now()
   583  		logged   = time.Now()
   584  		accounts uint64
   585  	)
   586  	enc := json.NewEncoder(os.Stdout)
   587  	enc.Encode(struct {
   588  		Root common.Hash `json:"root"`
   589  	}{root})
   590  	for accIt.Next() {
   591  		account, err := types.FullAccount(accIt.Account())
   592  		if err != nil {
   593  			return err
   594  		}
   595  		da := &state.DumpAccount{
   596  			Balance:     account.Balance.String(),
   597  			Nonce:       account.Nonce,
   598  			Root:        account.Root.Bytes(),
   599  			CodeHash:    account.CodeHash,
   600  			AddressHash: accIt.Hash().Bytes(),
   601  		}
   602  		if !conf.SkipCode && !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) {
   603  			da.Code = rawdb.ReadCode(db, common.BytesToHash(account.CodeHash))
   604  		}
   605  		if !conf.SkipStorage {
   606  			da.Storage = make(map[common.Hash]string)
   607  
   608  			stIt, err := snaptree.StorageIterator(root, accIt.Hash(), common.Hash{})
   609  			if err != nil {
   610  				return err
   611  			}
   612  			for stIt.Next() {
   613  				da.Storage[stIt.Hash()] = common.Bytes2Hex(stIt.Slot())
   614  			}
   615  		}
   616  		enc.Encode(da)
   617  		accounts++
   618  		if time.Since(logged) > 8*time.Second {
   619  			log.Info("Snapshot dumping in progress", "at", accIt.Hash(), "accounts", accounts,
   620  				"elapsed", common.PrettyDuration(time.Since(start)))
   621  			logged = time.Now()
   622  		}
   623  		if conf.Max > 0 && accounts >= conf.Max {
   624  			break
   625  		}
   626  	}
   627  	log.Info("Snapshot dumping complete", "accounts", accounts,
   628  		"elapsed", common.PrettyDuration(time.Since(start)))
   629  	return nil
   630  }
   631  
   632  // snapshotExportPreimages dumps the preimage data to a flat file.
   633  func snapshotExportPreimages(ctx *cli.Context) error {
   634  	if ctx.NArg() < 1 {
   635  		utils.Fatalf("This command requires an argument.")
   636  	}
   637  	stack, _ := makeConfigNode(ctx)
   638  	defer stack.Close()
   639  
   640  	chaindb := utils.MakeChainDatabase(ctx, stack, true)
   641  	defer chaindb.Close()
   642  
   643  	triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
   644  	defer triedb.Close()
   645  
   646  	var root common.Hash
   647  	if ctx.NArg() > 1 {
   648  		rootBytes := common.FromHex(ctx.Args().Get(1))
   649  		if len(rootBytes) != common.HashLength {
   650  			return fmt.Errorf("invalid hash: %s", ctx.Args().Get(1))
   651  		}
   652  		root = common.BytesToHash(rootBytes)
   653  	} else {
   654  		headBlock := rawdb.ReadHeadBlock(chaindb)
   655  		if headBlock == nil {
   656  			log.Error("Failed to load head block")
   657  			return errors.New("no head block")
   658  		}
   659  		root = headBlock.Root()
   660  	}
   661  	snapConfig := snapshot.Config{
   662  		CacheSize:  256,
   663  		Recovery:   false,
   664  		NoBuild:    true,
   665  		AsyncBuild: false,
   666  	}
   667  	snaptree, err := snapshot.New(snapConfig, chaindb, triedb, root)
   668  	if err != nil {
   669  		return err
   670  	}
   671  	return utils.ExportSnapshotPreimages(chaindb, snaptree, ctx.Args().First(), root)
   672  }
   673  
   674  // checkAccount iterates the snap data layers, and looks up the given account
   675  // across all layers.
   676  func checkAccount(ctx *cli.Context) error {
   677  	if ctx.NArg() != 1 {
   678  		return errors.New("need <address|hash> arg")
   679  	}
   680  	var (
   681  		hash common.Hash
   682  		addr common.Address
   683  	)
   684  	switch arg := ctx.Args().First(); len(arg) {
   685  	case 40, 42:
   686  		addr = common.HexToAddress(arg)
   687  		hash = crypto.Keccak256Hash(addr.Bytes())
   688  	case 64, 66:
   689  		hash = common.HexToHash(arg)
   690  	default:
   691  		return errors.New("malformed address or hash")
   692  	}
   693  	stack, _ := makeConfigNode(ctx)
   694  	defer stack.Close()
   695  	chaindb := utils.MakeChainDatabase(ctx, stack, true)
   696  	defer chaindb.Close()
   697  	start := time.Now()
   698  	log.Info("Checking difflayer journal", "address", addr, "hash", hash)
   699  	if err := snapshot.CheckJournalAccount(chaindb, hash); err != nil {
   700  		return err
   701  	}
   702  	log.Info("Checked the snapshot journalled storage", "time", common.PrettyDuration(time.Since(start)))
   703  	return nil
   704  }