trie: add Path to MissingNodeError

The light client trie iterator needs to know the path of the node that's
missing so it can retrieve a proof for it. NodeIterator.Path is not
sufficient because it is updated when the node is resolved and actually
visited by the iterator.

Also remove unused fields. They were added a long time ago before we
knew which fields would be needed for the light client.
This commit is contained in:
Felix Lange 2017-06-15 17:17:58 +02:00
parent 0b210b11e3
commit 3a2fceedb9
4 changed files with 20 additions and 36 deletions

View file

@ -23,24 +23,13 @@ import (
) )
// MissingNodeError is returned by the trie functions (TryGet, TryUpdate, TryDelete) // MissingNodeError is returned by the trie functions (TryGet, TryUpdate, TryDelete)
// in the case where a trie node is not present in the local database. Contains // in the case where a trie node is not present in the local database. It contains
// information necessary for retrieving the missing node through an ODR service. // information necessary for retrieving the missing node.
//
// NodeHash is the hash of the missing node
//
// RootHash is the original root of the trie that contains the node
//
// PrefixLen is the nibble length of the key prefix that leads from the root to
// the missing node
//
// SuffixLen is the nibble length of the remaining part of the key that hints on
// which further nodes should also be retrieved (can be zero when there are no
// such hints in the error message)
type MissingNodeError struct { type MissingNodeError struct {
RootHash, NodeHash common.Hash NodeHash common.Hash // hash of the missing node
PrefixLen, SuffixLen int Path []byte // hex-encoded path to the missing node
} }
func (err *MissingNodeError) Error() string { func (err *MissingNodeError) Error() string {
return fmt.Sprintf("Missing trie node %064x", err.NodeHash) return fmt.Sprintf("missing trie node %x (path %x)", err.NodeHash, err.Path)
} }

View file

@ -221,7 +221,7 @@ func (it *nodeIterator) peek(descend bool) (*nodeIteratorState, *int, []byte, er
if root != emptyRoot { if root != emptyRoot {
state.hash = root state.hash = root
} }
err := state.resolve(it.trie) err := state.resolve(it.trie, nil)
return state, nil, nil, err return state, nil, nil, err
} }
if !descend { if !descend {
@ -238,8 +238,8 @@ func (it *nodeIterator) peek(descend bool) (*nodeIteratorState, *int, []byte, er
} }
state, path, ok := it.nextChild(parent, ancestor) state, path, ok := it.nextChild(parent, ancestor)
if ok { if ok {
if err := state.resolve(it.trie); err != nil { if err := state.resolve(it.trie, path); err != nil {
return parent, &parent.index, it.path, err return parent, &parent.index, path, err
} }
return state, &parent.index, path, nil return state, &parent.index, path, nil
} }
@ -249,9 +249,9 @@ func (it *nodeIterator) peek(descend bool) (*nodeIteratorState, *int, []byte, er
return nil, nil, nil, iteratorEnd return nil, nil, nil, iteratorEnd
} }
func (st *nodeIteratorState) resolve(tr *Trie) error { func (st *nodeIteratorState) resolve(tr *Trie, path []byte) error {
if hash, ok := st.node.(hashNode); ok { if hash, ok := st.node.(hashNode); ok {
resolved, err := tr.resolveHash(hash, nil, nil) resolved, err := tr.resolveHash(hash, path)
if err != nil { if err != nil {
return err return err
} }

View file

@ -58,7 +58,7 @@ func (t *Trie) Prove(key []byte) []rlp.RawValue {
nodes = append(nodes, n) nodes = append(nodes, n)
case hashNode: case hashNode:
var err error var err error
tn, err = t.resolveHash(n, nil, nil) tn, err = t.resolveHash(n, nil)
if err != nil { if err != nil {
log.Error(fmt.Sprintf("Unhandled trie error: %v", err)) log.Error(fmt.Sprintf("Unhandled trie error: %v", err))
return nil return nil

View file

@ -116,7 +116,7 @@ func New(root common.Hash, db Database) (*Trie, error) {
if db == nil { if db == nil {
panic("trie.New: cannot use existing root without a database") panic("trie.New: cannot use existing root without a database")
} }
rootnode, err := trie.resolveHash(root[:], nil, nil) rootnode, err := trie.resolveHash(root[:], nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -180,7 +180,7 @@ func (t *Trie) tryGet(origNode node, key []byte, pos int) (value []byte, newnode
} }
return value, n, didResolve, err return value, n, didResolve, err
case hashNode: case hashNode:
child, err := t.resolveHash(n, key[:pos], key[pos:]) child, err := t.resolveHash(n, key[:pos])
if err != nil { if err != nil {
return nil, n, true, err return nil, n, true, err
} }
@ -283,7 +283,7 @@ func (t *Trie) insert(n node, prefix, key []byte, value node) (bool, node, error
// We've hit a part of the trie that isn't loaded yet. Load // We've hit a part of the trie that isn't loaded yet. Load
// the node and insert into it. This leaves all child nodes on // the node and insert into it. This leaves all child nodes on
// the path to the value in the trie. // the path to the value in the trie.
rn, err := t.resolveHash(n, prefix, key) rn, err := t.resolveHash(n, prefix)
if err != nil { if err != nil {
return false, nil, err return false, nil, err
} }
@ -388,7 +388,7 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) {
// shortNode{..., shortNode{...}}. Since the entry // shortNode{..., shortNode{...}}. Since the entry
// might not be loaded yet, resolve it just for this // might not be loaded yet, resolve it just for this
// check. // check.
cnode, err := t.resolve(n.Children[pos], prefix, []byte{byte(pos)}) cnode, err := t.resolve(n.Children[pos], prefix)
if err != nil { if err != nil {
return false, nil, err return false, nil, err
} }
@ -414,7 +414,7 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) {
// We've hit a part of the trie that isn't loaded yet. Load // We've hit a part of the trie that isn't loaded yet. Load
// the node and delete from it. This leaves all child nodes on // the node and delete from it. This leaves all child nodes on
// the path to the value in the trie. // the path to the value in the trie.
rn, err := t.resolveHash(n, prefix, key) rn, err := t.resolveHash(n, prefix)
if err != nil { if err != nil {
return false, nil, err return false, nil, err
} }
@ -436,24 +436,19 @@ func concat(s1 []byte, s2 ...byte) []byte {
return r return r
} }
func (t *Trie) resolve(n node, prefix, suffix []byte) (node, error) { func (t *Trie) resolve(n node, prefix []byte) (node, error) {
if n, ok := n.(hashNode); ok { if n, ok := n.(hashNode); ok {
return t.resolveHash(n, prefix, suffix) return t.resolveHash(n, prefix)
} }
return n, nil return n, nil
} }
func (t *Trie) resolveHash(n hashNode, prefix, suffix []byte) (node, error) { func (t *Trie) resolveHash(n hashNode, prefix []byte) (node, error) {
cacheMissCounter.Inc(1) cacheMissCounter.Inc(1)
enc, err := t.db.Get(n) enc, err := t.db.Get(n)
if err != nil || enc == nil { if err != nil || enc == nil {
return nil, &MissingNodeError{ return nil, &MissingNodeError{NodeHash: common.BytesToHash(n), Path: prefix}
RootHash: t.originalRoot,
NodeHash: common.BytesToHash(n),
PrefixLen: len(prefix),
SuffixLen: len(suffix),
}
} }
dec := mustDecodeNode(n, enc, t.cachegen) dec := mustDecodeNode(n, enc, t.cachegen)
return dec, nil return dec, nil