From b8d59d9c985feed9ec1d8851f65517c7e5c09deb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 16 Feb 2016 12:37:00 +0200 Subject: core/state, trie: switch iterator panics to error fields --- trie/iterator.go | 26 +++++++++++++++++++------- trie/sync_test.go | 18 ++++++------------ 2 files changed, 25 insertions(+), 19 deletions(-) (limited to 'trie') diff --git a/trie/iterator.go b/trie/iterator.go index d43b77698..ceef52ec8 100644 --- a/trie/iterator.go +++ b/trie/iterator.go @@ -169,6 +169,8 @@ type NodeIterator struct { Parent common.Hash // Hash of the first full ancestor node (nil if current is the root) Leaf bool // Flag whether the current node is a value (data) node LeafBlob []byte // Data blob contained within a leaf (otherwise nil) + + Error error // Failure set in case of an internal error in the iterator } // NewNodeIterator creates an post-order trie iterator. @@ -180,29 +182,38 @@ func NewNodeIterator(trie *Trie) *NodeIterator { } // Next moves the iterator to the next node, returning whether there are any -// further nodes. +// further nodes. In case of an internal error this method returns false and +// sets the Error field to the encountered failure. func (it *NodeIterator) Next() bool { - it.step() + // If the iterator failed previously, don't do anything + if it.Error != nil { + return false + } + // Otherwise step forward with the iterator and report any errors + if err := it.step(); err != nil { + it.Error = err + return false + } return it.retrieve() } // step moves the iterator to the next node of the trie. -func (it *NodeIterator) step() { +func (it *NodeIterator) step() error { // Abort if we reached the end of the iteration if it.trie == nil { - return + return nil } // Initialize the iterator if we've just started, or pop off the old node otherwise if len(it.stack) == 0 { it.stack = append(it.stack, &nodeIteratorState{node: it.trie.root, child: -1}) if it.stack[0].node == nil { - panic(fmt.Sprintf("root node missing: %x", it.trie.Root())) + return fmt.Errorf("root node missing: %x", it.trie.Root()) } } else { it.stack = it.stack[:len(it.stack)-1] if len(it.stack) == 0 { it.trie = nil - return + return nil } } // Continue iteration to the next child @@ -239,13 +250,14 @@ func (it *NodeIterator) step() { node, err := it.trie.resolveHash(hash, nil, nil) if err != nil { - panic(err) + return err } it.stack = append(it.stack, &nodeIteratorState{hash: common.BytesToHash(hash), node: node, parent: ancestor, child: -1}) } else { break } } + return nil } // retrieve pulls and caches the current trie node the iterator is traversing. diff --git a/trie/sync_test.go b/trie/sync_test.go index ce098d5a0..a81f7650e 100644 --- a/trie/sync_test.go +++ b/trie/sync_test.go @@ -18,7 +18,6 @@ package trie import ( "bytes" - "fmt" "testing" "github.com/ethereum/go-ethereum/common" @@ -80,25 +79,20 @@ func checkTrieContents(t *testing.T, db Database, root []byte, content map[strin } } -// checkTrieConsistency checks that all nodes in a trie and indeed present. -func checkTrieConsistency(db Database, root common.Hash) (failure error) { - // Capture any panics by the iterator - defer func() { - if r := recover(); r != nil { - failure = fmt.Errorf("%v", r) - } - }() +// checkTrieConsistency checks that all nodes in a trie are indeed present. +func checkTrieConsistency(db Database, root common.Hash) error { // Remove any potentially cached data from the test trie creation or previous checks globalCache.Clear() // Create and iterate a trie rooted in a subnode trie, err := New(root, db) if err != nil { - return + return nil // // Consider a non existent state consistent } - for it := NewNodeIterator(trie); it.Next(); { + it := NewNodeIterator(trie) + for it.Next() { } - return nil + return it.Error } // Tests that an empty trie is not scheduled for syncing. -- cgit v1.2.3