github.com/tirogen/go-ethereum@v1.10.12-0.20221226051715-250cfede41b6/core/state/snapshot/utils.go (about)

     1  // Copyright 2022 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser 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  // The go-ethereum library 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 Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package snapshot
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"time"
    23  
    24  	"github.com/tirogen/go-ethereum/common"
    25  	"github.com/tirogen/go-ethereum/core/rawdb"
    26  	"github.com/tirogen/go-ethereum/ethdb"
    27  	"github.com/tirogen/go-ethereum/log"
    28  	"github.com/tirogen/go-ethereum/rlp"
    29  )
    30  
    31  // CheckDanglingStorage iterates the snap storage data, and verifies that all
    32  // storage also has corresponding account data.
    33  func CheckDanglingStorage(chaindb ethdb.KeyValueStore) error {
    34  	if err := checkDanglingDiskStorage(chaindb); err != nil {
    35  		log.Error("Database check error", "err", err)
    36  	}
    37  	return checkDanglingMemStorage(chaindb)
    38  }
    39  
    40  // checkDanglingDiskStorage checks if there is any 'dangling' storage data in the
    41  // disk-backed snapshot layer.
    42  func checkDanglingDiskStorage(chaindb ethdb.KeyValueStore) error {
    43  	var (
    44  		lastReport = time.Now()
    45  		start      = time.Now()
    46  		lastKey    []byte
    47  		it         = rawdb.NewKeyLengthIterator(chaindb.NewIterator(rawdb.SnapshotStoragePrefix, nil), 1+2*common.HashLength)
    48  	)
    49  	log.Info("Checking dangling snapshot disk storage")
    50  
    51  	defer it.Release()
    52  	for it.Next() {
    53  		k := it.Key()
    54  		accKey := k[1:33]
    55  		if bytes.Equal(accKey, lastKey) {
    56  			// No need to look up for every slot
    57  			continue
    58  		}
    59  		lastKey = common.CopyBytes(accKey)
    60  		if time.Since(lastReport) > time.Second*8 {
    61  			log.Info("Iterating snap storage", "at", fmt.Sprintf("%#x", accKey), "elapsed", common.PrettyDuration(time.Since(start)))
    62  			lastReport = time.Now()
    63  		}
    64  		if data := rawdb.ReadAccountSnapshot(chaindb, common.BytesToHash(accKey)); len(data) == 0 {
    65  			log.Warn("Dangling storage - missing account", "account", fmt.Sprintf("%#x", accKey), "storagekey", fmt.Sprintf("%#x", k))
    66  			return fmt.Errorf("dangling snapshot storage account %#x", accKey)
    67  		}
    68  	}
    69  	log.Info("Verified the snapshot disk storage", "time", common.PrettyDuration(time.Since(start)), "err", it.Error())
    70  	return nil
    71  }
    72  
    73  // checkDanglingMemStorage checks if there is any 'dangling' storage in the journalled
    74  // snapshot difflayers.
    75  func checkDanglingMemStorage(db ethdb.KeyValueStore) error {
    76  	start := time.Now()
    77  	log.Info("Checking dangling journalled storage")
    78  	err := iterateJournal(db, func(pRoot, root common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error {
    79  		for accHash := range storage {
    80  			if _, ok := accounts[accHash]; !ok {
    81  				log.Error("Dangling storage - missing account", "account", fmt.Sprintf("%#x", accHash), "root", root)
    82  			}
    83  		}
    84  		return nil
    85  	})
    86  	if err != nil {
    87  		log.Info("Failed to resolve snapshot journal", "err", err)
    88  		return err
    89  	}
    90  	log.Info("Verified the snapshot journalled storage", "time", common.PrettyDuration(time.Since(start)))
    91  	return nil
    92  }
    93  
    94  // CheckJournalAccount shows information about an account, from the disk layer and
    95  // up through the diff layers.
    96  func CheckJournalAccount(db ethdb.KeyValueStore, hash common.Hash) error {
    97  	// Look up the disk layer first
    98  	baseRoot := rawdb.ReadSnapshotRoot(db)
    99  	fmt.Printf("Disklayer: Root: %x\n", baseRoot)
   100  	if data := rawdb.ReadAccountSnapshot(db, hash); data != nil {
   101  		account := new(Account)
   102  		if err := rlp.DecodeBytes(data, account); err != nil {
   103  			panic(err)
   104  		}
   105  		fmt.Printf("\taccount.nonce: %d\n", account.Nonce)
   106  		fmt.Printf("\taccount.balance: %x\n", account.Balance)
   107  		fmt.Printf("\taccount.root: %x\n", account.Root)
   108  		fmt.Printf("\taccount.codehash: %x\n", account.CodeHash)
   109  	}
   110  	// Check storage
   111  	{
   112  		it := rawdb.NewKeyLengthIterator(db.NewIterator(append(rawdb.SnapshotStoragePrefix, hash.Bytes()...), nil), 1+2*common.HashLength)
   113  		fmt.Printf("\tStorage:\n")
   114  		for it.Next() {
   115  			slot := it.Key()[33:]
   116  			fmt.Printf("\t\t%x: %x\n", slot, it.Value())
   117  		}
   118  		it.Release()
   119  	}
   120  	var depth = 0
   121  
   122  	return iterateJournal(db, func(pRoot, root common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) error {
   123  		_, a := accounts[hash]
   124  		_, b := destructs[hash]
   125  		_, c := storage[hash]
   126  		depth++
   127  		if !a && !b && !c {
   128  			return nil
   129  		}
   130  		fmt.Printf("Disklayer+%d: Root: %x, parent %x\n", depth, root, pRoot)
   131  		if data, ok := accounts[hash]; ok {
   132  			account := new(Account)
   133  			if err := rlp.DecodeBytes(data, account); err != nil {
   134  				panic(err)
   135  			}
   136  			fmt.Printf("\taccount.nonce: %d\n", account.Nonce)
   137  			fmt.Printf("\taccount.balance: %x\n", account.Balance)
   138  			fmt.Printf("\taccount.root: %x\n", account.Root)
   139  			fmt.Printf("\taccount.codehash: %x\n", account.CodeHash)
   140  		}
   141  		if _, ok := destructs[hash]; ok {
   142  			fmt.Printf("\t Destructed!")
   143  		}
   144  		if data, ok := storage[hash]; ok {
   145  			fmt.Printf("\tStorage\n")
   146  			for k, v := range data {
   147  				fmt.Printf("\t\t%x: %x\n", k, v)
   148  			}
   149  		}
   150  		return nil
   151  	})
   152  }