core/state, trie: surface iterator entry hashes
This commit is contained in:
		| @@ -33,8 +33,11 @@ type NodeIterator struct { | |||||||
|  |  | ||||||
| 	stateIt *trie.NodeIterator // Primary iterator for the global state trie | 	stateIt *trie.NodeIterator // Primary iterator for the global state trie | ||||||
| 	dataIt  *trie.NodeIterator // Secondary iterator for the data trie of a contract | 	dataIt  *trie.NodeIterator // Secondary iterator for the data trie of a contract | ||||||
|  |  | ||||||
|  | 	codeHash common.Hash // Hash of the contract source code | ||||||
| 	code     []byte      // Source code associated with a contract | 	code     []byte      // Source code associated with a contract | ||||||
|  |  | ||||||
|  | 	Hash  common.Hash // Hash of the current entry being iterated (nil if not standalone) | ||||||
| 	Entry interface{} // Current state entry being iterated (internal representation) | 	Entry interface{} // Current state entry being iterated (internal representation) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -103,6 +106,7 @@ func (it *NodeIterator) step() { | |||||||
| 		it.dataIt = nil | 		it.dataIt = nil | ||||||
| 	} | 	} | ||||||
| 	if bytes.Compare(account.CodeHash, emptyCodeHash) != 0 { | 	if bytes.Compare(account.CodeHash, emptyCodeHash) != 0 { | ||||||
|  | 		it.codeHash = common.BytesToHash(account.CodeHash) | ||||||
| 		it.code, err = it.state.db.Get(account.CodeHash) | 		it.code, err = it.state.db.Get(account.CodeHash) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			panic(fmt.Sprintf("code %x: %v", account.CodeHash, err)) | 			panic(fmt.Sprintf("code %x: %v", account.CodeHash, err)) | ||||||
| @@ -114,7 +118,7 @@ func (it *NodeIterator) step() { | |||||||
| // The method returns whether there are any more data left for inspection. | // The method returns whether there are any more data left for inspection. | ||||||
| func (it *NodeIterator) retrieve() bool { | func (it *NodeIterator) retrieve() bool { | ||||||
| 	// Clear out any previously set values | 	// Clear out any previously set values | ||||||
| 	it.Entry = nil | 	it.Hash, it.Entry = common.Hash{}, nil | ||||||
|  |  | ||||||
| 	// If the iteration's done, return no available data | 	// If the iteration's done, return no available data | ||||||
| 	if it.state == nil { | 	if it.state == nil { | ||||||
| @@ -123,11 +127,11 @@ func (it *NodeIterator) retrieve() bool { | |||||||
| 	// Otherwise retrieve the current entry | 	// Otherwise retrieve the current entry | ||||||
| 	switch { | 	switch { | ||||||
| 	case it.dataIt != nil: | 	case it.dataIt != nil: | ||||||
| 		it.Entry = it.dataIt.Node | 		it.Hash, it.Entry = it.dataIt.Hash, it.dataIt.Node | ||||||
| 	case it.code != nil: | 	case it.code != nil: | ||||||
| 		it.Entry = it.code | 		it.Hash, it.Entry = it.codeHash, it.code | ||||||
| 	case it.stateIt != nil: | 	case it.stateIt != nil: | ||||||
| 		it.Entry = it.stateIt.Node | 		it.Hash, it.Entry = it.stateIt.Hash, it.stateIt.Node | ||||||
| 	} | 	} | ||||||
| 	return true | 	return true | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										57
									
								
								core/state/iterator_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								core/state/iterator_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | |||||||
|  | // Copyright 2015 The go-ethereum Authors | ||||||
|  | // This file is part of the go-ethereum library. | ||||||
|  | // | ||||||
|  | // The go-ethereum library is free software: you can redistribute it and/or modify | ||||||
|  | // it under the terms of the GNU Lesser General Public License as published by | ||||||
|  | // the Free Software Foundation, either version 3 of the License, or | ||||||
|  | // (at your option) any later version. | ||||||
|  | // | ||||||
|  | // The go-ethereum library is distributed in the hope that it will be useful, | ||||||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||
|  | // GNU Lesser General Public License for more details. | ||||||
|  | // | ||||||
|  | // You should have received a copy of the GNU Lesser General Public License | ||||||
|  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  | ||||||
|  | package state | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/ethereum/go-ethereum/common" | ||||||
|  | 	"github.com/ethereum/go-ethereum/ethdb" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Tests that the node iterator indeed walks over the entire database contents. | ||||||
|  | func TestNodeIteratorCoverage(t *testing.T) { | ||||||
|  | 	// Create some arbitrary test state to iterate | ||||||
|  | 	db, root, _ := makeTestState() | ||||||
|  |  | ||||||
|  | 	state, err := New(root, db) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("failed to create state trie at %x: %v", root, err) | ||||||
|  | 	} | ||||||
|  | 	// Gather all the node hashes found by the iterator | ||||||
|  | 	hashes := make(map[common.Hash]struct{}) | ||||||
|  | 	for it := NewNodeIterator(state); it.Next(); { | ||||||
|  | 		if it.Hash != (common.Hash{}) { | ||||||
|  | 			hashes[it.Hash] = struct{}{} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// Cross check the hashes and the database itself | ||||||
|  | 	for hash, _ := range hashes { | ||||||
|  | 		if _, err := db.Get(hash.Bytes()); err != nil { | ||||||
|  | 			t.Errorf("failed to retrieve reported node %x: %v", hash, err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	for _, key := range db.(*ethdb.MemDatabase).Keys() { | ||||||
|  | 		if bytes.HasPrefix(key, []byte("secure-key-")) { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if _, ok := hashes[common.BytesToHash(key)]; !ok { | ||||||
|  | 			t.Errorf("state entry not reported %x", key) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -116,8 +116,7 @@ func checkStateConsistency(db ethdb.Database, root common.Hash) (failure error) | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	it := NewNodeIterator(state) | 	for it := NewNodeIterator(state); it.Next(); { | ||||||
| 	for it.Next() { |  | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|   | |||||||
| @@ -20,6 +20,7 @@ import ( | |||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  |  | ||||||
|  | 	"github.com/ethereum/go-ethereum/common" | ||||||
| 	"github.com/ethereum/go-ethereum/logger" | 	"github.com/ethereum/go-ethereum/logger" | ||||||
| 	"github.com/ethereum/go-ethereum/logger/glog" | 	"github.com/ethereum/go-ethereum/logger/glog" | ||||||
| ) | ) | ||||||
| @@ -152,6 +153,7 @@ func (self *Iterator) key(node interface{}) []byte { | |||||||
| // nodeIteratorState represents the iteration state at one particular node of the | // nodeIteratorState represents the iteration state at one particular node of the | ||||||
| // trie, which can be resumed at a later invocation. | // trie, which can be resumed at a later invocation. | ||||||
| type nodeIteratorState struct { | type nodeIteratorState struct { | ||||||
|  | 	hash  common.Hash // Hash of the node being iterated (nil if not standalone) | ||||||
| 	node  node        // Trie node being iterated | 	node  node        // Trie node being iterated | ||||||
| 	child int         // Child to be processed next | 	child int         // Child to be processed next | ||||||
| } | } | ||||||
| @@ -161,6 +163,7 @@ type NodeIterator struct { | |||||||
| 	trie  *Trie                // Trie being iterated | 	trie  *Trie                // Trie being iterated | ||||||
| 	stack []*nodeIteratorState // Hierarchy of trie nodes persisting the iteration state | 	stack []*nodeIteratorState // Hierarchy of trie nodes persisting the iteration state | ||||||
|  |  | ||||||
|  | 	Hash     common.Hash // Hash of the current node being iterated (nil if not standalone) | ||||||
| 	Node     node        // Current node being iterated (internal representation) | 	Node     node        // Current node being iterated (internal representation) | ||||||
| 	Leaf     bool        // Flag whether the current node is a value (data) node | 	Leaf     bool        // Flag whether the current node is a value (data) node | ||||||
| 	LeafBlob []byte      // Data blob contained within a leaf (otherwise nil) | 	LeafBlob []byte      // Data blob contained within a leaf (otherwise nil) | ||||||
| @@ -221,18 +224,18 @@ func (it *NodeIterator) step() { | |||||||
| 			} | 			} | ||||||
| 			parent.child++ | 			parent.child++ | ||||||
| 			it.stack = append(it.stack, &nodeIteratorState{node: node.Val, child: -1}) | 			it.stack = append(it.stack, &nodeIteratorState{node: node.Val, child: -1}) | ||||||
| 		} else if node, ok := parent.node.(hashNode); ok { | 		} else if hash, ok := parent.node.(hashNode); ok { | ||||||
| 			// Hash node, resolve the hash child from the database, then the node itself | 			// Hash node, resolve the hash child from the database, then the node itself | ||||||
| 			if parent.child >= 0 { | 			if parent.child >= 0 { | ||||||
| 				break | 				break | ||||||
| 			} | 			} | ||||||
| 			parent.child++ | 			parent.child++ | ||||||
|  |  | ||||||
| 			node, err := it.trie.resolveHash(node, nil, nil) | 			node, err := it.trie.resolveHash(hash, nil, nil) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				panic(err) | 				panic(err) | ||||||
| 			} | 			} | ||||||
| 			it.stack = append(it.stack, &nodeIteratorState{node: node, child: -1}) | 			it.stack = append(it.stack, &nodeIteratorState{hash: common.BytesToHash(hash), node: node, child: -1}) | ||||||
| 		} else { | 		} else { | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
| @@ -246,14 +249,16 @@ func (it *NodeIterator) step() { | |||||||
| // The method returns whether there are any more data left for inspection. | // The method returns whether there are any more data left for inspection. | ||||||
| func (it *NodeIterator) retrieve() bool { | func (it *NodeIterator) retrieve() bool { | ||||||
| 	// Clear out any previously set values | 	// Clear out any previously set values | ||||||
| 	it.Node, it.Leaf, it.LeafBlob = nil, false, nil | 	it.Hash, it.Node, it.Leaf, it.LeafBlob = common.Hash{}, nil, false, nil | ||||||
|  |  | ||||||
| 	// If the iteration's done, return no available data | 	// If the iteration's done, return no available data | ||||||
| 	if it.trie == nil { | 	if it.trie == nil { | ||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
| 	// Otherwise retrieve the current node and resolve leaf accessors | 	// Otherwise retrieve the current node and resolve leaf accessors | ||||||
| 	it.Node = it.stack[len(it.stack)-1].node | 	state := it.stack[len(it.stack)-1] | ||||||
|  |  | ||||||
|  | 	it.Hash, it.Node = state.hash, state.node | ||||||
| 	if value, ok := it.Node.(valueNode); ok { | 	if value, ok := it.Node.(valueNode); ok { | ||||||
| 		it.Leaf, it.LeafBlob = true, []byte(value) | 		it.Leaf, it.LeafBlob = true, []byte(value) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -16,7 +16,12 @@ | |||||||
|  |  | ||||||
| package trie | package trie | ||||||
|  |  | ||||||
| import "testing" | import ( | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/ethereum/go-ethereum/common" | ||||||
|  | 	"github.com/ethereum/go-ethereum/ethdb" | ||||||
|  | ) | ||||||
|  |  | ||||||
| func TestIterator(t *testing.T) { | func TestIterator(t *testing.T) { | ||||||
| 	trie := newEmpty() | 	trie := newEmpty() | ||||||
| @@ -47,3 +52,28 @@ func TestIterator(t *testing.T) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Tests that the node iterator indeed walks over the entire database contents. | ||||||
|  | func TestNodeIteratorCoverage(t *testing.T) { | ||||||
|  | 	// Create some arbitrary test trie to iterate | ||||||
|  | 	db, trie, _ := makeTestTrie() | ||||||
|  |  | ||||||
|  | 	// Gather all the node hashes found by the iterator | ||||||
|  | 	hashes := make(map[common.Hash]struct{}) | ||||||
|  | 	for it := NewNodeIterator(trie); it.Next(); { | ||||||
|  | 		if it.Hash != (common.Hash{}) { | ||||||
|  | 			hashes[it.Hash] = struct{}{} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// Cross check the hashes and the database itself | ||||||
|  | 	for hash, _ := range hashes { | ||||||
|  | 		if _, err := db.Get(hash.Bytes()); err != nil { | ||||||
|  | 			t.Errorf("failed to retrieve reported node %x: %v", hash, err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	for _, key := range db.(*ethdb.MemDatabase).Keys() { | ||||||
|  | 		if _, ok := hashes[common.BytesToHash(key)]; !ok { | ||||||
|  | 			t.Errorf("state entry not reported %x", key) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -96,8 +96,7 @@ func checkTrieConsistency(db Database, root common.Hash) (failure error) { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	it := NewNodeIterator(trie) | 	for it := NewNodeIterator(trie); it.Next(); { | ||||||
| 	for it.Next() { |  | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user