github.com/ethereum/go-ethereum@v1.14.3/core/state/iterator_test.go (about)

     1  // Copyright 2016 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 state
    18  
    19  import (
    20  	"testing"
    21  
    22  	"github.com/ethereum/go-ethereum/common"
    23  	"github.com/ethereum/go-ethereum/core/rawdb"
    24  	"github.com/ethereum/go-ethereum/crypto"
    25  )
    26  
    27  // Tests that the node iterator indeed walks over the entire database contents.
    28  func TestNodeIteratorCoverage(t *testing.T) {
    29  	testNodeIteratorCoverage(t, rawdb.HashScheme)
    30  	testNodeIteratorCoverage(t, rawdb.PathScheme)
    31  }
    32  
    33  func testNodeIteratorCoverage(t *testing.T, scheme string) {
    34  	// Create some arbitrary test state to iterate
    35  	db, sdb, ndb, root, _ := makeTestState(scheme)
    36  	ndb.Commit(root, false)
    37  
    38  	state, err := New(root, sdb, nil)
    39  	if err != nil {
    40  		t.Fatalf("failed to create state trie at %x: %v", root, err)
    41  	}
    42  	// Gather all the node hashes found by the iterator
    43  	hashes := make(map[common.Hash]struct{})
    44  	for it := newNodeIterator(state); it.Next(); {
    45  		if it.Hash != (common.Hash{}) {
    46  			hashes[it.Hash] = struct{}{}
    47  		}
    48  	}
    49  	// Check in-disk nodes
    50  	var (
    51  		seenNodes = make(map[common.Hash]struct{})
    52  		seenCodes = make(map[common.Hash]struct{})
    53  	)
    54  	it := db.NewIterator(nil, nil)
    55  	for it.Next() {
    56  		ok, hash := isTrieNode(scheme, it.Key(), it.Value())
    57  		if !ok {
    58  			continue
    59  		}
    60  		seenNodes[hash] = struct{}{}
    61  	}
    62  	it.Release()
    63  
    64  	// Check in-disk codes
    65  	it = db.NewIterator(nil, nil)
    66  	for it.Next() {
    67  		ok, hash := rawdb.IsCodeKey(it.Key())
    68  		if !ok {
    69  			continue
    70  		}
    71  		if _, ok := hashes[common.BytesToHash(hash)]; !ok {
    72  			t.Errorf("state entry not reported %x", it.Key())
    73  		}
    74  		seenCodes[common.BytesToHash(hash)] = struct{}{}
    75  	}
    76  	it.Release()
    77  
    78  	// Cross check the iterated hashes and the database/nodepool content
    79  	for hash := range hashes {
    80  		_, ok := seenNodes[hash]
    81  		if !ok {
    82  			_, ok = seenCodes[hash]
    83  		}
    84  		if !ok {
    85  			t.Errorf("failed to retrieve reported node %x", hash)
    86  		}
    87  	}
    88  }
    89  
    90  // isTrieNode is a helper function which reports if the provided
    91  // database entry belongs to a trie node or not.
    92  func isTrieNode(scheme string, key, val []byte) (bool, common.Hash) {
    93  	if scheme == rawdb.HashScheme {
    94  		if rawdb.IsLegacyTrieNode(key, val) {
    95  			return true, common.BytesToHash(key)
    96  		}
    97  	} else {
    98  		ok := rawdb.IsAccountTrieNode(key)
    99  		if ok {
   100  			return true, crypto.Keccak256Hash(val)
   101  		}
   102  		ok = rawdb.IsStorageTrieNode(key)
   103  		if ok {
   104  			return true, crypto.Keccak256Hash(val)
   105  		}
   106  	}
   107  	return false, common.Hash{}
   108  }