github.com/ethereum/go-ethereum@v1.14.4-0.20240516095835-473ee8fc07a3/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  	"time"
    26  
    27  	"github.com/ethereum/go-ethereum/cmd/utils"
    28  	"github.com/ethereum/go-ethereum/common"
    29  	"github.com/ethereum/go-ethereum/core/rawdb"
    30  	"github.com/ethereum/go-ethereum/core/state"
    31  	"github.com/ethereum/go-ethereum/core/state/pruner"
    32  	"github.com/ethereum/go-ethereum/core/state/snapshot"
    33  	"github.com/ethereum/go-ethereum/core/types"
    34  	"github.com/ethereum/go-ethereum/crypto"
    35  	"github.com/ethereum/go-ethereum/internal/flags"
    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: flags.Merge([]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:     flags.Merge(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:     flags.Merge(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 a specific account",
    95  				ArgsUsage: "<address | hash>",
    96  				Action:    checkAccount,
    97  				Flags:     flags.Merge(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:     flags.Merge(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:     flags.Merge(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: flags.Merge([]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  	snapConfig := snapshot.Config{
   224  		CacheSize:  256,
   225  		Recovery:   false,
   226  		NoBuild:    true,
   227  		AsyncBuild: false,
   228  	}
   229  	snaptree, err := snapshot.New(snapConfig, chaindb, triedb, headBlock.Root())
   230  	if err != nil {
   231  		log.Error("Failed to open snapshot tree", "err", err)
   232  		return err
   233  	}
   234  	if ctx.NArg() > 1 {
   235  		log.Error("Too many arguments given")
   236  		return errors.New("too many arguments")
   237  	}
   238  	var root = headBlock.Root()
   239  	if ctx.NArg() == 1 {
   240  		root, err = parseRoot(ctx.Args().First())
   241  		if err != nil {
   242  			log.Error("Failed to resolve state root", "err", err)
   243  			return err
   244  		}
   245  	}
   246  	if err := snaptree.Verify(root); err != nil {
   247  		log.Error("Failed to verify state", "root", root, "err", err)
   248  		return err
   249  	}
   250  	log.Info("Verified the state", "root", root)
   251  	return snapshot.CheckDanglingStorage(chaindb)
   252  }
   253  
   254  // checkDanglingStorage iterates the snap storage data, and verifies that all
   255  // storage also has corresponding account data.
   256  func checkDanglingStorage(ctx *cli.Context) error {
   257  	stack, _ := makeConfigNode(ctx)
   258  	defer stack.Close()
   259  
   260  	db := utils.MakeChainDatabase(ctx, stack, true)
   261  	defer db.Close()
   262  	return snapshot.CheckDanglingStorage(db)
   263  }
   264  
   265  // traverseState is a helper function used for pruning verification.
   266  // Basically it just iterates the trie, ensure all nodes and associated
   267  // contract codes are present.
   268  func traverseState(ctx *cli.Context) error {
   269  	stack, _ := makeConfigNode(ctx)
   270  	defer stack.Close()
   271  
   272  	chaindb := utils.MakeChainDatabase(ctx, stack, true)
   273  	defer chaindb.Close()
   274  
   275  	triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
   276  	defer triedb.Close()
   277  
   278  	headBlock := rawdb.ReadHeadBlock(chaindb)
   279  	if headBlock == nil {
   280  		log.Error("Failed to load head block")
   281  		return errors.New("no head block")
   282  	}
   283  	if ctx.NArg() > 1 {
   284  		log.Error("Too many arguments given")
   285  		return errors.New("too many arguments")
   286  	}
   287  	var (
   288  		root common.Hash
   289  		err  error
   290  	)
   291  	if ctx.NArg() == 1 {
   292  		root, err = parseRoot(ctx.Args().First())
   293  		if err != nil {
   294  			log.Error("Failed to resolve state root", "err", err)
   295  			return err
   296  		}
   297  		log.Info("Start traversing the state", "root", root)
   298  	} else {
   299  		root = headBlock.Root()
   300  		log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64())
   301  	}
   302  	t, err := trie.NewStateTrie(trie.StateTrieID(root), triedb)
   303  	if err != nil {
   304  		log.Error("Failed to open trie", "root", root, "err", err)
   305  		return err
   306  	}
   307  	var (
   308  		accounts   int
   309  		slots      int
   310  		codes      int
   311  		lastReport time.Time
   312  		start      = time.Now()
   313  	)
   314  	acctIt, err := t.NodeIterator(nil)
   315  	if err != nil {
   316  		log.Error("Failed to open iterator", "root", root, "err", err)
   317  		return err
   318  	}
   319  	accIter := trie.NewIterator(acctIt)
   320  	for accIter.Next() {
   321  		accounts += 1
   322  		var acc types.StateAccount
   323  		if err := rlp.DecodeBytes(accIter.Value, &acc); err != nil {
   324  			log.Error("Invalid account encountered during traversal", "err", err)
   325  			return err
   326  		}
   327  		if acc.Root != types.EmptyRootHash {
   328  			id := trie.StorageTrieID(root, common.BytesToHash(accIter.Key), acc.Root)
   329  			storageTrie, err := trie.NewStateTrie(id, triedb)
   330  			if err != nil {
   331  				log.Error("Failed to open storage trie", "root", acc.Root, "err", err)
   332  				return err
   333  			}
   334  			storageIt, err := storageTrie.NodeIterator(nil)
   335  			if err != nil {
   336  				log.Error("Failed to open storage iterator", "root", acc.Root, "err", err)
   337  				return err
   338  			}
   339  			storageIter := trie.NewIterator(storageIt)
   340  			for storageIter.Next() {
   341  				slots += 1
   342  
   343  				if time.Since(lastReport) > time.Second*8 {
   344  					log.Info("Traversing state", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
   345  					lastReport = time.Now()
   346  				}
   347  			}
   348  			if storageIter.Err != nil {
   349  				log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Err)
   350  				return storageIter.Err
   351  			}
   352  		}
   353  		if !bytes.Equal(acc.CodeHash, types.EmptyCodeHash.Bytes()) {
   354  			if !rawdb.HasCode(chaindb, common.BytesToHash(acc.CodeHash)) {
   355  				log.Error("Code is missing", "hash", common.BytesToHash(acc.CodeHash))
   356  				return errors.New("missing code")
   357  			}
   358  			codes += 1
   359  		}
   360  		if time.Since(lastReport) > time.Second*8 {
   361  			log.Info("Traversing state", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
   362  			lastReport = time.Now()
   363  		}
   364  	}
   365  	if accIter.Err != nil {
   366  		log.Error("Failed to traverse state trie", "root", root, "err", accIter.Err)
   367  		return accIter.Err
   368  	}
   369  	log.Info("State is complete", "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
   370  	return nil
   371  }
   372  
   373  // traverseRawState is a helper function used for pruning verification.
   374  // Basically it just iterates the trie, ensure all nodes and associated
   375  // contract codes are present. It's basically identical to traverseState
   376  // but it will check each trie node.
   377  func traverseRawState(ctx *cli.Context) error {
   378  	stack, _ := makeConfigNode(ctx)
   379  	defer stack.Close()
   380  
   381  	chaindb := utils.MakeChainDatabase(ctx, stack, true)
   382  	defer chaindb.Close()
   383  
   384  	triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
   385  	defer triedb.Close()
   386  
   387  	headBlock := rawdb.ReadHeadBlock(chaindb)
   388  	if headBlock == nil {
   389  		log.Error("Failed to load head block")
   390  		return errors.New("no head block")
   391  	}
   392  	if ctx.NArg() > 1 {
   393  		log.Error("Too many arguments given")
   394  		return errors.New("too many arguments")
   395  	}
   396  	var (
   397  		root common.Hash
   398  		err  error
   399  	)
   400  	if ctx.NArg() == 1 {
   401  		root, err = parseRoot(ctx.Args().First())
   402  		if err != nil {
   403  			log.Error("Failed to resolve state root", "err", err)
   404  			return err
   405  		}
   406  		log.Info("Start traversing the state", "root", root)
   407  	} else {
   408  		root = headBlock.Root()
   409  		log.Info("Start traversing the state", "root", root, "number", headBlock.NumberU64())
   410  	}
   411  	t, err := trie.NewStateTrie(trie.StateTrieID(root), triedb)
   412  	if err != nil {
   413  		log.Error("Failed to open trie", "root", root, "err", err)
   414  		return err
   415  	}
   416  	var (
   417  		nodes      int
   418  		accounts   int
   419  		slots      int
   420  		codes      int
   421  		lastReport time.Time
   422  		start      = time.Now()
   423  		hasher     = crypto.NewKeccakState()
   424  		got        = make([]byte, 32)
   425  	)
   426  	accIter, err := t.NodeIterator(nil)
   427  	if err != nil {
   428  		log.Error("Failed to open iterator", "root", root, "err", err)
   429  		return err
   430  	}
   431  	reader, err := triedb.Reader(root)
   432  	if err != nil {
   433  		log.Error("State is non-existent", "root", root)
   434  		return nil
   435  	}
   436  	for accIter.Next(true) {
   437  		nodes += 1
   438  		node := accIter.Hash()
   439  
   440  		// Check the present for non-empty hash node(embedded node doesn't
   441  		// have their own hash).
   442  		if node != (common.Hash{}) {
   443  			blob, _ := reader.Node(common.Hash{}, accIter.Path(), node)
   444  			if len(blob) == 0 {
   445  				log.Error("Missing trie node(account)", "hash", node)
   446  				return errors.New("missing account")
   447  			}
   448  			hasher.Reset()
   449  			hasher.Write(blob)
   450  			hasher.Read(got)
   451  			if !bytes.Equal(got, node.Bytes()) {
   452  				log.Error("Invalid trie node(account)", "hash", node.Hex(), "value", blob)
   453  				return errors.New("invalid account node")
   454  			}
   455  		}
   456  		// If it's a leaf node, yes we are touching an account,
   457  		// dig into the storage trie further.
   458  		if accIter.Leaf() {
   459  			accounts += 1
   460  			var acc types.StateAccount
   461  			if err := rlp.DecodeBytes(accIter.LeafBlob(), &acc); err != nil {
   462  				log.Error("Invalid account encountered during traversal", "err", err)
   463  				return errors.New("invalid account")
   464  			}
   465  			if acc.Root != types.EmptyRootHash {
   466  				id := trie.StorageTrieID(root, common.BytesToHash(accIter.LeafKey()), acc.Root)
   467  				storageTrie, err := trie.NewStateTrie(id, triedb)
   468  				if err != nil {
   469  					log.Error("Failed to open storage trie", "root", acc.Root, "err", err)
   470  					return errors.New("missing storage trie")
   471  				}
   472  				storageIter, err := storageTrie.NodeIterator(nil)
   473  				if err != nil {
   474  					log.Error("Failed to open storage iterator", "root", acc.Root, "err", err)
   475  					return err
   476  				}
   477  				for storageIter.Next(true) {
   478  					nodes += 1
   479  					node := storageIter.Hash()
   480  
   481  					// Check the presence for non-empty hash node(embedded node doesn't
   482  					// have their own hash).
   483  					if node != (common.Hash{}) {
   484  						blob, _ := reader.Node(common.BytesToHash(accIter.LeafKey()), storageIter.Path(), node)
   485  						if len(blob) == 0 {
   486  							log.Error("Missing trie node(storage)", "hash", node)
   487  							return errors.New("missing storage")
   488  						}
   489  						hasher.Reset()
   490  						hasher.Write(blob)
   491  						hasher.Read(got)
   492  						if !bytes.Equal(got, node.Bytes()) {
   493  							log.Error("Invalid trie node(storage)", "hash", node.Hex(), "value", blob)
   494  							return errors.New("invalid storage node")
   495  						}
   496  					}
   497  					// Bump the counter if it's leaf node.
   498  					if storageIter.Leaf() {
   499  						slots += 1
   500  					}
   501  					if time.Since(lastReport) > time.Second*8 {
   502  						log.Info("Traversing state", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
   503  						lastReport = time.Now()
   504  					}
   505  				}
   506  				if storageIter.Error() != nil {
   507  					log.Error("Failed to traverse storage trie", "root", acc.Root, "err", storageIter.Error())
   508  					return storageIter.Error()
   509  				}
   510  			}
   511  			if !bytes.Equal(acc.CodeHash, types.EmptyCodeHash.Bytes()) {
   512  				if !rawdb.HasCode(chaindb, common.BytesToHash(acc.CodeHash)) {
   513  					log.Error("Code is missing", "account", common.BytesToHash(accIter.LeafKey()))
   514  					return errors.New("missing code")
   515  				}
   516  				codes += 1
   517  			}
   518  			if time.Since(lastReport) > time.Second*8 {
   519  				log.Info("Traversing state", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
   520  				lastReport = time.Now()
   521  			}
   522  		}
   523  	}
   524  	if accIter.Error() != nil {
   525  		log.Error("Failed to traverse state trie", "root", root, "err", accIter.Error())
   526  		return accIter.Error()
   527  	}
   528  	log.Info("State is complete", "nodes", nodes, "accounts", accounts, "slots", slots, "codes", codes, "elapsed", common.PrettyDuration(time.Since(start)))
   529  	return nil
   530  }
   531  
   532  func parseRoot(input string) (common.Hash, error) {
   533  	var h common.Hash
   534  	if err := h.UnmarshalText([]byte(input)); err != nil {
   535  		return h, err
   536  	}
   537  	return h, nil
   538  }
   539  
   540  func dumpState(ctx *cli.Context) error {
   541  	stack, _ := makeConfigNode(ctx)
   542  	defer stack.Close()
   543  
   544  	db := utils.MakeChainDatabase(ctx, stack, true)
   545  	defer db.Close()
   546  
   547  	conf, root, err := parseDumpConfig(ctx, stack, db)
   548  	if err != nil {
   549  		return err
   550  	}
   551  	triedb := utils.MakeTrieDatabase(ctx, db, false, true, false)
   552  	defer triedb.Close()
   553  
   554  	snapConfig := snapshot.Config{
   555  		CacheSize:  256,
   556  		Recovery:   false,
   557  		NoBuild:    true,
   558  		AsyncBuild: false,
   559  	}
   560  	snaptree, err := snapshot.New(snapConfig, db, triedb, root)
   561  	if err != nil {
   562  		return err
   563  	}
   564  	accIt, err := snaptree.AccountIterator(root, common.BytesToHash(conf.Start))
   565  	if err != nil {
   566  		return err
   567  	}
   568  	defer accIt.Release()
   569  
   570  	log.Info("Snapshot dumping started", "root", root)
   571  	var (
   572  		start    = time.Now()
   573  		logged   = time.Now()
   574  		accounts uint64
   575  	)
   576  	enc := json.NewEncoder(os.Stdout)
   577  	enc.Encode(struct {
   578  		Root common.Hash `json:"root"`
   579  	}{root})
   580  	for accIt.Next() {
   581  		account, err := types.FullAccount(accIt.Account())
   582  		if err != nil {
   583  			return err
   584  		}
   585  		da := &state.DumpAccount{
   586  			Balance:     account.Balance.String(),
   587  			Nonce:       account.Nonce,
   588  			Root:        account.Root.Bytes(),
   589  			CodeHash:    account.CodeHash,
   590  			AddressHash: accIt.Hash().Bytes(),
   591  		}
   592  		if !conf.SkipCode && !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) {
   593  			da.Code = rawdb.ReadCode(db, common.BytesToHash(account.CodeHash))
   594  		}
   595  		if !conf.SkipStorage {
   596  			da.Storage = make(map[common.Hash]string)
   597  
   598  			stIt, err := snaptree.StorageIterator(root, accIt.Hash(), common.Hash{})
   599  			if err != nil {
   600  				return err
   601  			}
   602  			for stIt.Next() {
   603  				da.Storage[stIt.Hash()] = common.Bytes2Hex(stIt.Slot())
   604  			}
   605  		}
   606  		enc.Encode(da)
   607  		accounts++
   608  		if time.Since(logged) > 8*time.Second {
   609  			log.Info("Snapshot dumping in progress", "at", accIt.Hash(), "accounts", accounts,
   610  				"elapsed", common.PrettyDuration(time.Since(start)))
   611  			logged = time.Now()
   612  		}
   613  		if conf.Max > 0 && accounts >= conf.Max {
   614  			break
   615  		}
   616  	}
   617  	log.Info("Snapshot dumping complete", "accounts", accounts,
   618  		"elapsed", common.PrettyDuration(time.Since(start)))
   619  	return nil
   620  }
   621  
   622  // snapshotExportPreimages dumps the preimage data to a flat file.
   623  func snapshotExportPreimages(ctx *cli.Context) error {
   624  	if ctx.NArg() < 1 {
   625  		utils.Fatalf("This command requires an argument.")
   626  	}
   627  	stack, _ := makeConfigNode(ctx)
   628  	defer stack.Close()
   629  
   630  	chaindb := utils.MakeChainDatabase(ctx, stack, true)
   631  	defer chaindb.Close()
   632  
   633  	triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
   634  	defer triedb.Close()
   635  
   636  	var root common.Hash
   637  	if ctx.NArg() > 1 {
   638  		rootBytes := common.FromHex(ctx.Args().Get(1))
   639  		if len(rootBytes) != common.HashLength {
   640  			return fmt.Errorf("invalid hash: %s", ctx.Args().Get(1))
   641  		}
   642  		root = common.BytesToHash(rootBytes)
   643  	} else {
   644  		headBlock := rawdb.ReadHeadBlock(chaindb)
   645  		if headBlock == nil {
   646  			log.Error("Failed to load head block")
   647  			return errors.New("no head block")
   648  		}
   649  		root = headBlock.Root()
   650  	}
   651  	snapConfig := snapshot.Config{
   652  		CacheSize:  256,
   653  		Recovery:   false,
   654  		NoBuild:    true,
   655  		AsyncBuild: false,
   656  	}
   657  	snaptree, err := snapshot.New(snapConfig, chaindb, triedb, root)
   658  	if err != nil {
   659  		return err
   660  	}
   661  	return utils.ExportSnapshotPreimages(chaindb, snaptree, ctx.Args().First(), root)
   662  }
   663  
   664  // checkAccount iterates the snap data layers, and looks up the given account
   665  // across all layers.
   666  func checkAccount(ctx *cli.Context) error {
   667  	if ctx.NArg() != 1 {
   668  		return errors.New("need <address|hash> arg")
   669  	}
   670  	var (
   671  		hash common.Hash
   672  		addr common.Address
   673  	)
   674  	switch arg := ctx.Args().First(); len(arg) {
   675  	case 40, 42:
   676  		addr = common.HexToAddress(arg)
   677  		hash = crypto.Keccak256Hash(addr.Bytes())
   678  	case 64, 66:
   679  		hash = common.HexToHash(arg)
   680  	default:
   681  		return errors.New("malformed address or hash")
   682  	}
   683  	stack, _ := makeConfigNode(ctx)
   684  	defer stack.Close()
   685  	chaindb := utils.MakeChainDatabase(ctx, stack, true)
   686  	defer chaindb.Close()
   687  	start := time.Now()
   688  	log.Info("Checking difflayer journal", "address", addr, "hash", hash)
   689  	if err := snapshot.CheckJournalAccount(chaindb, hash); err != nil {
   690  		return err
   691  	}
   692  	log.Info("Checked the snapshot journalled storage", "time", common.PrettyDuration(time.Since(start)))
   693  	return nil
   694  }