forked from cerc-io/plugeth
core/state, trie: switch iterator panics to error fields
This commit is contained in:
parent
151c7bef41
commit
b8d59d9c98
@ -41,6 +41,8 @@ type NodeIterator struct {
|
|||||||
Hash common.Hash // Hash of the current entry being iterated (nil if not standalone)
|
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)
|
||||||
Parent common.Hash // Hash of the first full ancestor node (nil if current is the root)
|
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.
|
// 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
|
// 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 {
|
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()
|
return it.retrieve()
|
||||||
}
|
}
|
||||||
|
|
||||||
// step moves the iterator to the next entry of the state trie.
|
// 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
|
// Abort if we reached the end of the iteration
|
||||||
if it.state == nil {
|
if it.state == nil {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
// Initialize the iterator if we've just started
|
// Initialize the iterator if we've just started
|
||||||
if it.stateIt == nil {
|
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 we had data nodes previously, we surely have at least state nodes
|
||||||
if it.dataIt != nil {
|
if it.dataIt != nil {
|
||||||
if cont := it.dataIt.Next(); !cont {
|
if cont := it.dataIt.Next(); !cont {
|
||||||
|
if it.dataIt.Error != nil {
|
||||||
|
return it.dataIt.Error
|
||||||
|
}
|
||||||
it.dataIt = nil
|
it.dataIt = nil
|
||||||
}
|
}
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
// If we had source code previously, discard that
|
// If we had source code previously, discard that
|
||||||
if it.code != nil {
|
if it.code != nil {
|
||||||
it.code = nil
|
it.code = nil
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
// Step to the next state trie node, terminating if we're out of nodes
|
// Step to the next state trie node, terminating if we're out of nodes
|
||||||
if cont := it.stateIt.Next(); !cont {
|
if cont := it.stateIt.Next(); !cont {
|
||||||
|
if it.stateIt.Error != nil {
|
||||||
|
return it.stateIt.Error
|
||||||
|
}
|
||||||
it.state, it.stateIt = nil, nil
|
it.state, it.stateIt = nil, nil
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
// If the state trie node is an internal entry, leave as is
|
// If the state trie node is an internal entry, leave as is
|
||||||
if !it.stateIt.Leaf {
|
if !it.stateIt.Leaf {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
// Otherwise we've reached an account node, initiate data iteration
|
// Otherwise we've reached an account node, initiate data iteration
|
||||||
var account struct {
|
var account struct {
|
||||||
@ -95,13 +112,12 @@ func (it *NodeIterator) step() {
|
|||||||
Root common.Hash
|
Root common.Hash
|
||||||
CodeHash []byte
|
CodeHash []byte
|
||||||
}
|
}
|
||||||
err := rlp.Decode(bytes.NewReader(it.stateIt.LeafBlob), &account)
|
if err := rlp.Decode(bytes.NewReader(it.stateIt.LeafBlob), &account); err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
dataTrie, err := trie.New(account.Root, it.state.db)
|
dataTrie, err := trie.New(account.Root, it.state.db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
it.dataIt = trie.NewNodeIterator(dataTrie)
|
it.dataIt = trie.NewNodeIterator(dataTrie)
|
||||||
if !it.dataIt.Next() {
|
if !it.dataIt.Next() {
|
||||||
@ -111,10 +127,11 @@ func (it *NodeIterator) step() {
|
|||||||
it.codeHash = common.BytesToHash(account.CodeHash)
|
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))
|
return fmt.Errorf("code %x: %v", account.CodeHash, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
it.accountHash = it.stateIt.Parent
|
it.accountHash = it.stateIt.Parent
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// retrieve pulls and caches the current state entry the iterator is traversing.
|
// retrieve pulls and caches the current state entry the iterator is traversing.
|
||||||
|
@ -18,7 +18,6 @@ package state
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"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.
|
// checkStateConsistency checks that all nodes in a state trie are indeed present.
|
||||||
func checkStateConsistency(db ethdb.Database, root common.Hash) (failure error) {
|
func checkStateConsistency(db ethdb.Database, root common.Hash) error {
|
||||||
// Capture any panics by the iterator
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
failure = fmt.Errorf("%v", r)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
// Remove any potentially cached data from the test state creation or previous checks
|
// Remove any potentially cached data from the test state creation or previous checks
|
||||||
trie.ClearGlobalCache()
|
trie.ClearGlobalCache()
|
||||||
|
|
||||||
// Create and iterate a state trie rooted in a sub-node
|
// Create and iterate a state trie rooted in a sub-node
|
||||||
if _, err := db.Get(root.Bytes()); err != nil {
|
if _, err := db.Get(root.Bytes()); err != nil {
|
||||||
return
|
return nil // Consider a non existent state consistent
|
||||||
}
|
}
|
||||||
state, err := New(root, db)
|
state, err := New(root, db)
|
||||||
if err != nil {
|
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.
|
// Tests that an empty state is not scheduled for syncing.
|
||||||
|
@ -169,6 +169,8 @@ type NodeIterator struct {
|
|||||||
Parent common.Hash // Hash of the first full ancestor node (nil if current is the root)
|
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
|
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)
|
||||||
|
|
||||||
|
Error error // Failure set in case of an internal error in the iterator
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNodeIterator creates an post-order trie 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
|
// 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 {
|
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()
|
return it.retrieve()
|
||||||
}
|
}
|
||||||
|
|
||||||
// step moves the iterator to the next node of the trie.
|
// 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
|
// Abort if we reached the end of the iteration
|
||||||
if it.trie == nil {
|
if it.trie == nil {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
// Initialize the iterator if we've just started, or pop off the old node otherwise
|
// Initialize the iterator if we've just started, or pop off the old node otherwise
|
||||||
if len(it.stack) == 0 {
|
if len(it.stack) == 0 {
|
||||||
it.stack = append(it.stack, &nodeIteratorState{node: it.trie.root, child: -1})
|
it.stack = append(it.stack, &nodeIteratorState{node: it.trie.root, child: -1})
|
||||||
if it.stack[0].node == nil {
|
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 {
|
} else {
|
||||||
it.stack = it.stack[:len(it.stack)-1]
|
it.stack = it.stack[:len(it.stack)-1]
|
||||||
if len(it.stack) == 0 {
|
if len(it.stack) == 0 {
|
||||||
it.trie = nil
|
it.trie = nil
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Continue iteration to the next child
|
// Continue iteration to the next child
|
||||||
@ -239,13 +250,14 @@ func (it *NodeIterator) step() {
|
|||||||
|
|
||||||
node, err := it.trie.resolveHash(hash, nil, nil)
|
node, err := it.trie.resolveHash(hash, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
it.stack = append(it.stack, &nodeIteratorState{hash: common.BytesToHash(hash), node: node, parent: ancestor, child: -1})
|
it.stack = append(it.stack, &nodeIteratorState{hash: common.BytesToHash(hash), node: node, parent: ancestor, child: -1})
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// retrieve pulls and caches the current trie node the iterator is traversing.
|
// retrieve pulls and caches the current trie node the iterator is traversing.
|
||||||
|
@ -18,7 +18,6 @@ package trie
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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.
|
// checkTrieConsistency checks that all nodes in a trie are indeed present.
|
||||||
func checkTrieConsistency(db Database, root common.Hash) (failure error) {
|
func checkTrieConsistency(db Database, root common.Hash) error {
|
||||||
// Capture any panics by the iterator
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
failure = fmt.Errorf("%v", r)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
// Remove any potentially cached data from the test trie creation or previous checks
|
// Remove any potentially cached data from the test trie creation or previous checks
|
||||||
globalCache.Clear()
|
globalCache.Clear()
|
||||||
|
|
||||||
// Create and iterate a trie rooted in a subnode
|
// Create and iterate a trie rooted in a subnode
|
||||||
trie, err := New(root, db)
|
trie, err := New(root, db)
|
||||||
if err != nil {
|
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.
|
// Tests that an empty trie is not scheduled for syncing.
|
||||||
|
Loading…
Reference in New Issue
Block a user