diff options
author | Péter Szilágyi <peterke@gmail.com> | 2016-02-16 18:37:00 +0800 |
---|---|---|
committer | Péter Szilágyi <peterke@gmail.com> | 2016-02-16 18:37:00 +0800 |
commit | b8d59d9c985feed9ec1d8851f65517c7e5c09deb (patch) | |
tree | 5ceb6da2603a0fceb849e9ecd4aea641c6ebc816 /core | |
parent | 151c7bef41ed96d9aace3dba235ad2db5fe26e03 (diff) | |
download | dexon-b8d59d9c985feed9ec1d8851f65517c7e5c09deb.tar.gz dexon-b8d59d9c985feed9ec1d8851f65517c7e5c09deb.tar.zst dexon-b8d59d9c985feed9ec1d8851f65517c7e5c09deb.zip |
core/state, trie: switch iterator panics to error fields
Diffstat (limited to 'core')
-rw-r--r-- | core/state/iterator.go | 43 | ||||
-rw-r--r-- | core/state/sync_test.go | 20 |
2 files changed, 37 insertions, 26 deletions
diff --git a/core/state/iterator.go b/core/state/iterator.go index a051e5e01..9d8a69b7c 100644 --- a/core/state/iterator.go +++ b/core/state/iterator.go @@ -41,6 +41,8 @@ type NodeIterator struct { Hash common.Hash // Hash of the current entry being iterated (nil if not standalone) Entry interface{} // Current state entry being iterated (internal representation) Parent common.Hash // Hash of the first full ancestor node (nil if current is the root) + + Error error // Failure set in case of an internal error in the iterator } // NewNodeIterator creates an post-order state node iterator. @@ -51,17 +53,26 @@ func NewNodeIterator(state *StateDB) *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 entry of the state trie. -func (it *NodeIterator) step() { +func (it *NodeIterator) step() error { // Abort if we reached the end of the iteration if it.state == nil { - return + return nil } // Initialize the iterator if we've just started if it.stateIt == nil { @@ -70,23 +81,29 @@ func (it *NodeIterator) step() { // If we had data nodes previously, we surely have at least state nodes if it.dataIt != nil { if cont := it.dataIt.Next(); !cont { + if it.dataIt.Error != nil { + return it.dataIt.Error + } it.dataIt = nil } - return + return nil } // If we had source code previously, discard that if it.code != nil { it.code = nil - return + return nil } // Step to the next state trie node, terminating if we're out of nodes if cont := it.stateIt.Next(); !cont { + if it.stateIt.Error != nil { + return it.stateIt.Error + } it.state, it.stateIt = nil, nil - return + return nil } // If the state trie node is an internal entry, leave as is if !it.stateIt.Leaf { - return + return nil } // Otherwise we've reached an account node, initiate data iteration var account struct { @@ -95,13 +112,12 @@ func (it *NodeIterator) step() { Root common.Hash CodeHash []byte } - err := rlp.Decode(bytes.NewReader(it.stateIt.LeafBlob), &account) - if err != nil { - panic(err) + if err := rlp.Decode(bytes.NewReader(it.stateIt.LeafBlob), &account); err != nil { + return err } dataTrie, err := trie.New(account.Root, it.state.db) if err != nil { - panic(err) + return err } it.dataIt = trie.NewNodeIterator(dataTrie) if !it.dataIt.Next() { @@ -111,10 +127,11 @@ func (it *NodeIterator) step() { it.codeHash = common.BytesToHash(account.CodeHash) it.code, err = it.state.db.Get(account.CodeHash) if err != nil { - panic(fmt.Sprintf("code %x: %v", account.CodeHash, err)) + return fmt.Errorf("code %x: %v", account.CodeHash, err) } } it.accountHash = it.stateIt.Parent + return nil } // retrieve pulls and caches the current state entry the iterator is traversing. diff --git a/core/state/sync_test.go b/core/state/sync_test.go index 727a276a5..a2a1edbdb 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -18,7 +18,6 @@ package state import ( "bytes" - "fmt" "math/big" "testing" @@ -97,28 +96,23 @@ func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accou } } -// checkStateConsistency checks that all nodes in a state trie and indeed present. -func checkStateConsistency(db ethdb.Database, root common.Hash) (failure error) { - // Capture any panics by the iterator - defer func() { - if r := recover(); r != nil { - failure = fmt.Errorf("%v", r) - } - }() +// checkStateConsistency checks that all nodes in a state trie are indeed present. +func checkStateConsistency(db ethdb.Database, root common.Hash) error { // Remove any potentially cached data from the test state creation or previous checks trie.ClearGlobalCache() // Create and iterate a state trie rooted in a sub-node if _, err := db.Get(root.Bytes()); err != nil { - return + return nil // Consider a non existent state consistent } state, err := New(root, db) if err != nil { - return + return err } - for it := NewNodeIterator(state); it.Next(); { + it := NewNodeIterator(state) + for it.Next() { } - return nil + return it.Error } // Tests that an empty state is not scheduled for syncing. |