Commit 55430b6e authored by Gary Rong's avatar Gary Rong

trie: implement NodeBlob API for trie iterator

This functionality is needed in new path-based storage scheme, but
can be implemented in a seperate PR though.

When an account is deleted, then all the storage slots should be
nuked out from the disk as well. In hash-based storage scheme they
are still left in the disk but in new scheme, they will be iterated
and marked as deleted.

But why the NodeBlob API is needed in this scenario? Because when
the node is marked deleted, the previous value is also required to
be recorded to construct the reverse diff.
parent 6c3513c0
...@@ -86,6 +86,10 @@ type NodeIterator interface { ...@@ -86,6 +86,10 @@ type NodeIterator interface {
// For leaf nodes, the last element of the path is the 'terminator symbol' 0x10. // For leaf nodes, the last element of the path is the 'terminator symbol' 0x10.
Path() []byte Path() []byte
// NodeBlob returns the rlp-encoded value of the current iterated node.
// If the node is an embedded node in its parent, nil is returned then.
NodeBlob() []byte
// Leaf returns true iff the current node is a leaf node. // Leaf returns true iff the current node is a leaf node.
Leaf() bool Leaf() bool
...@@ -224,6 +228,18 @@ func (it *nodeIterator) Path() []byte { ...@@ -224,6 +228,18 @@ func (it *nodeIterator) Path() []byte {
return it.path return it.path
} }
func (it *nodeIterator) NodeBlob() []byte {
if it.Hash() == (common.Hash{}) {
return nil // skip the non-standalone node
}
blob, err := it.resolveBlob(it.Hash().Bytes(), it.Path())
if err != nil {
it.err = err
return nil
}
return blob
}
func (it *nodeIterator) Error() error { func (it *nodeIterator) Error() error {
if it.err == errIteratorEnd { if it.err == errIteratorEnd {
return nil return nil
...@@ -362,6 +378,15 @@ func (it *nodeIterator) resolveHash(hash hashNode, path []byte) (node, error) { ...@@ -362,6 +378,15 @@ func (it *nodeIterator) resolveHash(hash hashNode, path []byte) (node, error) {
return resolved, err return resolved, err
} }
func (it *nodeIterator) resolveBlob(hash hashNode, path []byte) ([]byte, error) {
if it.resolver != nil {
if blob, err := it.resolver.Get(hash); err == nil && len(blob) > 0 {
return blob, nil
}
}
return it.trie.resolveBlob(hash, path)
}
func (st *nodeIteratorState) resolve(it *nodeIterator, path []byte) error { func (st *nodeIteratorState) resolve(it *nodeIterator, path []byte) error {
if hash, ok := st.node.(hashNode); ok { if hash, ok := st.node.(hashNode); ok {
resolved, err := it.resolveHash(hash, path) resolved, err := it.resolveHash(hash, path)
...@@ -549,6 +574,10 @@ func (it *differenceIterator) Path() []byte { ...@@ -549,6 +574,10 @@ func (it *differenceIterator) Path() []byte {
return it.b.Path() return it.b.Path()
} }
func (it *differenceIterator) NodeBlob() []byte {
return it.b.NodeBlob()
}
func (it *differenceIterator) AddResolver(resolver ethdb.KeyValueReader) { func (it *differenceIterator) AddResolver(resolver ethdb.KeyValueReader) {
panic("not implemented") panic("not implemented")
} }
...@@ -660,6 +689,10 @@ func (it *unionIterator) Path() []byte { ...@@ -660,6 +689,10 @@ func (it *unionIterator) Path() []byte {
return (*it.items)[0].Path() return (*it.items)[0].Path()
} }
func (it *unionIterator) NodeBlob() []byte {
return (*it.items)[0].NodeBlob()
}
func (it *unionIterator) AddResolver(resolver ethdb.KeyValueReader) { func (it *unionIterator) AddResolver(resolver ethdb.KeyValueReader) {
panic("not implemented") panic("not implemented")
} }
......
...@@ -521,3 +521,54 @@ func TestNodeIteratorLargeTrie(t *testing.T) { ...@@ -521,3 +521,54 @@ func TestNodeIteratorLargeTrie(t *testing.T) {
t.Fatalf("Too many lookups during seek, have %d want %d", have, want) t.Fatalf("Too many lookups during seek, have %d want %d", have, want)
} }
} }
func TestIteratorNodeBlob(t *testing.T) {
var (
db = memorydb.New()
triedb = NewDatabase(db)
trie, _ = New(common.Hash{}, triedb)
)
vals := []struct{ k, v string }{
{"do", "verb"},
{"ether", "wookiedoo"},
{"horse", "stallion"},
{"shaman", "horse"},
{"doge", "coin"},
{"dog", "puppy"},
{"somethingveryoddindeedthis is", "myothernodedata"},
}
all := make(map[string]string)
for _, val := range vals {
all[val.k] = val.v
trie.Update([]byte(val.k), []byte(val.v))
}
trie.Commit(nil)
triedb.Cap(0)
found := make(map[common.Hash][]byte)
it := trie.NodeIterator(nil)
for it.Next(true) {
if it.Hash() == (common.Hash{}) {
continue
}
found[it.Hash()] = it.NodeBlob()
}
dbIter := db.NewIterator(nil, nil)
defer dbIter.Release()
var count int
for dbIter.Next() {
got, present := found[common.BytesToHash(dbIter.Key())]
if !present {
t.Fatalf("Miss trie node %v", dbIter.Key())
}
if !bytes.Equal(got, dbIter.Value()) {
t.Fatalf("Unexpected trie node want %v got %v", dbIter.Value(), got)
}
count += 1
}
if count != len(found) {
t.Fatal("Find extra trie node via iterator")
}
}
...@@ -514,6 +514,15 @@ func (t *Trie) resolveHash(n hashNode, prefix []byte) (node, error) { ...@@ -514,6 +514,15 @@ func (t *Trie) resolveHash(n hashNode, prefix []byte) (node, error) {
return nil, &MissingNodeError{NodeHash: hash, Path: prefix} return nil, &MissingNodeError{NodeHash: hash, Path: prefix}
} }
func (t *Trie) resolveBlob(n hashNode, prefix []byte) ([]byte, error) {
hash := common.BytesToHash(n)
blob, _ := t.db.Node(hash)
if len(blob) != 0 {
return blob, nil
}
return nil, &MissingNodeError{NodeHash: hash, Path: prefix}
}
// Hash returns the root hash of the trie. It does not write to the // Hash returns the root hash of the trie. It does not write to the
// database and can be used even if the trie doesn't have one. // database and can be used even if the trie doesn't have one.
func (t *Trie) Hash() common.Hash { func (t *Trie) Hash() common.Hash {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment