forked from cerc-io/plugeth
cmd, core/state, eth, tests, trie: improve state reader (#27428)
The state availability is checked during the creation of a state reader.
- In hash-based database, if the specified root node does not exist on disk disk, then
the state reader won't be created and an error will be returned.
- In path-based database, if the specified state layer is not available, then the
state reader won't be created and an error will be returned.
This change also contains a stricter semantics regarding the `Commit` operation: once it has been performed, the trie is no longer usable, and certain operations will return an error.
This commit is contained in:
@@ -113,8 +113,8 @@ func NewDatabaseWithConfig(diskdb ethdb.Database, config *Config) *Database {
|
||||
}
|
||||
|
||||
// Reader returns a reader for accessing all trie nodes with provided state root.
|
||||
// Nil is returned in case the state is not available.
|
||||
func (db *Database) Reader(blockRoot common.Hash) Reader {
|
||||
// An error will be returned if the requested state is not available.
|
||||
func (db *Database) Reader(blockRoot common.Hash) (Reader, error) {
|
||||
return db.backend.(*hashdb.Database).Reader(blockRoot)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,11 +17,17 @@
|
||||
package trie
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// ErrCommitted is returned when a already committed trie is requested for usage.
|
||||
// The potential usages can be `Get`, `Update`, `Delete`, `NodeIterator`, `Prove`
|
||||
// and so on.
|
||||
var ErrCommitted = errors.New("trie is already committed")
|
||||
|
||||
// MissingNodeError is returned by the trie functions (Get, Update, Delete)
|
||||
// in the case where a trie node is not present in the local database. It contains
|
||||
// information necessary for retrieving the missing node.
|
||||
|
||||
+24
-17
@@ -34,7 +34,7 @@ import (
|
||||
|
||||
func TestEmptyIterator(t *testing.T) {
|
||||
trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
iter := trie.NodeIterator(nil)
|
||||
iter := trie.MustNodeIterator(nil)
|
||||
|
||||
seen := make(map[string]struct{})
|
||||
for iter.Next(true) {
|
||||
@@ -67,7 +67,7 @@ func TestIterator(t *testing.T) {
|
||||
|
||||
trie, _ = New(TrieID(root), db)
|
||||
found := make(map[string]string)
|
||||
it := NewIterator(trie.NodeIterator(nil))
|
||||
it := NewIterator(trie.MustNodeIterator(nil))
|
||||
for it.Next() {
|
||||
found[string(it.Key)] = string(it.Value)
|
||||
}
|
||||
@@ -101,7 +101,7 @@ func TestIteratorLargeData(t *testing.T) {
|
||||
vals[string(value2.k)] = value2
|
||||
}
|
||||
|
||||
it := NewIterator(trie.NodeIterator(nil))
|
||||
it := NewIterator(trie.MustNodeIterator(nil))
|
||||
for it.Next() {
|
||||
vals[string(it.Key)].t = true
|
||||
}
|
||||
@@ -139,7 +139,7 @@ func testNodeIteratorCoverage(t *testing.T, scheme string) {
|
||||
|
||||
// Gather all the node hashes found by the iterator
|
||||
var elements = make(map[common.Hash]iterationElement)
|
||||
for it := trie.NodeIterator(nil); it.Next(true); {
|
||||
for it := trie.MustNodeIterator(nil); it.Next(true); {
|
||||
if it.Hash() != (common.Hash{}) {
|
||||
elements[it.Hash()] = iterationElement{
|
||||
hash: it.Hash(),
|
||||
@@ -149,8 +149,12 @@ func testNodeIteratorCoverage(t *testing.T, scheme string) {
|
||||
}
|
||||
}
|
||||
// Cross check the hashes and the database itself
|
||||
reader, err := nodeDb.Reader(trie.Hash())
|
||||
if err != nil {
|
||||
t.Fatalf("state is not available %x", trie.Hash())
|
||||
}
|
||||
for _, element := range elements {
|
||||
if blob, err := nodeDb.Reader(trie.Hash()).Node(common.Hash{}, element.path, element.hash); err != nil {
|
||||
if blob, err := reader.Node(common.Hash{}, element.path, element.hash); err != nil {
|
||||
t.Errorf("failed to retrieve reported node %x: %v", element.hash, err)
|
||||
} else if !bytes.Equal(blob, element.blob) {
|
||||
t.Errorf("node blob is different, want %v got %v", element.blob, blob)
|
||||
@@ -210,19 +214,19 @@ func TestIteratorSeek(t *testing.T) {
|
||||
}
|
||||
|
||||
// Seek to the middle.
|
||||
it := NewIterator(trie.NodeIterator([]byte("fab")))
|
||||
it := NewIterator(trie.MustNodeIterator([]byte("fab")))
|
||||
if err := checkIteratorOrder(testdata1[4:], it); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Seek to a non-existent key.
|
||||
it = NewIterator(trie.NodeIterator([]byte("barc")))
|
||||
it = NewIterator(trie.MustNodeIterator([]byte("barc")))
|
||||
if err := checkIteratorOrder(testdata1[1:], it); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Seek beyond the end.
|
||||
it = NewIterator(trie.NodeIterator([]byte("z")))
|
||||
it = NewIterator(trie.MustNodeIterator([]byte("z")))
|
||||
if err := checkIteratorOrder(nil, it); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -264,7 +268,7 @@ func TestDifferenceIterator(t *testing.T) {
|
||||
trieb, _ = New(TrieID(rootB), dbb)
|
||||
|
||||
found := make(map[string]string)
|
||||
di, _ := NewDifferenceIterator(triea.NodeIterator(nil), trieb.NodeIterator(nil))
|
||||
di, _ := NewDifferenceIterator(triea.MustNodeIterator(nil), trieb.MustNodeIterator(nil))
|
||||
it := NewIterator(di)
|
||||
for it.Next() {
|
||||
found[string(it.Key)] = string(it.Value)
|
||||
@@ -305,7 +309,7 @@ func TestUnionIterator(t *testing.T) {
|
||||
dbb.Update(rootB, types.EmptyRootHash, trienode.NewWithNodeSet(nodesB))
|
||||
trieb, _ = New(TrieID(rootB), dbb)
|
||||
|
||||
di, _ := NewUnionIterator([]NodeIterator{triea.NodeIterator(nil), trieb.NodeIterator(nil)})
|
||||
di, _ := NewUnionIterator([]NodeIterator{triea.MustNodeIterator(nil), trieb.MustNodeIterator(nil)})
|
||||
it := NewIterator(di)
|
||||
|
||||
all := []struct{ k, v string }{
|
||||
@@ -344,7 +348,7 @@ func TestIteratorNoDups(t *testing.T) {
|
||||
for _, val := range testdata1 {
|
||||
tr.MustUpdate([]byte(val.k), []byte(val.v))
|
||||
}
|
||||
checkIteratorNoDups(t, tr.NodeIterator(nil), nil)
|
||||
checkIteratorNoDups(t, tr.MustNodeIterator(nil), nil)
|
||||
}
|
||||
|
||||
// This test checks that nodeIterator.Next can be retried after inserting missing trie nodes.
|
||||
@@ -369,7 +373,7 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool, scheme string) {
|
||||
tdb.Commit(root, false)
|
||||
}
|
||||
tr, _ = New(TrieID(root), tdb)
|
||||
wantNodeCount := checkIteratorNoDups(t, tr.NodeIterator(nil), nil)
|
||||
wantNodeCount := checkIteratorNoDups(t, tr.MustNodeIterator(nil), nil)
|
||||
|
||||
var (
|
||||
paths [][]byte
|
||||
@@ -428,7 +432,7 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool, scheme string) {
|
||||
}
|
||||
// Iterate until the error is hit.
|
||||
seen := make(map[string]bool)
|
||||
it := tr.NodeIterator(nil)
|
||||
it := tr.MustNodeIterator(nil)
|
||||
checkIteratorNoDups(t, it, seen)
|
||||
missing, ok := it.Error().(*MissingNodeError)
|
||||
if !ok || missing.NodeHash != rhash {
|
||||
@@ -496,7 +500,7 @@ func testIteratorContinueAfterSeekError(t *testing.T, memonly bool, scheme strin
|
||||
}
|
||||
// Create a new iterator that seeks to "bars". Seeking can't proceed because
|
||||
// the node is missing.
|
||||
it := tr.NodeIterator([]byte("bars"))
|
||||
it := tr.MustNodeIterator([]byte("bars"))
|
||||
missing, ok := it.Error().(*MissingNodeError)
|
||||
if !ok {
|
||||
t.Fatal("want MissingNodeError, got", it.Error())
|
||||
@@ -602,7 +606,10 @@ func makeLargeTestTrie() (*Database, *StateTrie, *loggingDb) {
|
||||
}
|
||||
root, nodes := trie.Commit(false)
|
||||
triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
|
||||
triedb.Commit(root, false)
|
||||
|
||||
// Return the generated trie
|
||||
trie, _ = NewStateTrie(TrieID(root), triedb)
|
||||
return triedb, trie, logDb
|
||||
}
|
||||
|
||||
@@ -614,8 +621,8 @@ func TestNodeIteratorLargeTrie(t *testing.T) {
|
||||
// Do a seek operation
|
||||
trie.NodeIterator(common.FromHex("0x77667766776677766778855885885885"))
|
||||
// master: 24 get operations
|
||||
// this pr: 5 get operations
|
||||
if have, want := logDb.getCount, uint64(5); have != want {
|
||||
// this pr: 6 get operations
|
||||
if have, want := logDb.getCount, uint64(6); have != want {
|
||||
t.Fatalf("Too many lookups during seek, have %d want %d", have, want)
|
||||
}
|
||||
}
|
||||
@@ -646,7 +653,7 @@ func testIteratorNodeBlob(t *testing.T, scheme string) {
|
||||
|
||||
var found = make(map[common.Hash][]byte)
|
||||
trie, _ = New(TrieID(root), triedb)
|
||||
it := trie.NodeIterator(nil)
|
||||
it := trie.MustNodeIterator(nil)
|
||||
for it.Next(true) {
|
||||
if it.Hash() == (common.Hash{}) {
|
||||
continue
|
||||
|
||||
@@ -34,6 +34,10 @@ import (
|
||||
// nodes of the longest existing prefix of the key (at least the root node), ending
|
||||
// with the node that proves the absence of the key.
|
||||
func (t *Trie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
|
||||
// Short circuit if the trie is already committed and not usable.
|
||||
if t.committed {
|
||||
return ErrCommitted
|
||||
}
|
||||
// Collect all nodes on the path to key.
|
||||
var (
|
||||
prefix []byte
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ func makeProvers(trie *Trie) []func(key []byte) *memorydb.Database {
|
||||
// Create a leaf iterator based Merkle prover
|
||||
provers = append(provers, func(key []byte) *memorydb.Database {
|
||||
proof := memorydb.New()
|
||||
if it := NewIterator(trie.NodeIterator(key)); it.Next() && bytes.Equal(key, it.Key) {
|
||||
if it := NewIterator(trie.MustNodeIterator(key)); it.Next() && bytes.Equal(key, it.Key) {
|
||||
for _, p := range it.Prove() {
|
||||
proof.Put(crypto.Keccak256(p), p)
|
||||
}
|
||||
|
||||
+9
-3
@@ -250,12 +250,18 @@ func (t *StateTrie) Copy() *StateTrie {
|
||||
}
|
||||
}
|
||||
|
||||
// NodeIterator returns an iterator that returns nodes of the underlying trie. Iteration
|
||||
// starts at the key after the given start key.
|
||||
func (t *StateTrie) NodeIterator(start []byte) NodeIterator {
|
||||
// NodeIterator returns an iterator that returns nodes of the underlying trie.
|
||||
// Iteration starts at the key after the given start key.
|
||||
func (t *StateTrie) NodeIterator(start []byte) (NodeIterator, error) {
|
||||
return t.trie.NodeIterator(start)
|
||||
}
|
||||
|
||||
// MustNodeIterator is a wrapper of NodeIterator and will omit any encountered
|
||||
// error but just print out an error message.
|
||||
func (t *StateTrie) MustNodeIterator(start []byte) NodeIterator {
|
||||
return t.trie.MustNodeIterator(start)
|
||||
}
|
||||
|
||||
// hashKey returns the hash of key as an ephemeral buffer.
|
||||
// The caller must not hold onto the return value because it will become
|
||||
// invalid on the next call to hashKey or secKey.
|
||||
|
||||
+41
-11
@@ -94,7 +94,7 @@ func checkTrieConsistency(db ethdb.Database, scheme string, root common.Hash) er
|
||||
if err != nil {
|
||||
return nil // Consider a non existent state consistent
|
||||
}
|
||||
it := trie.NodeIterator(nil)
|
||||
it := trie.MustNodeIterator(nil)
|
||||
for it.Next(true) {
|
||||
}
|
||||
return it.Error()
|
||||
@@ -159,12 +159,16 @@ func testIterativeSync(t *testing.T, count int, bypath bool, scheme string) {
|
||||
syncPath: NewSyncPath([]byte(paths[i])),
|
||||
})
|
||||
}
|
||||
reader, err := srcDb.Reader(srcTrie.Hash())
|
||||
if err != nil {
|
||||
t.Fatalf("State is not available %x", srcTrie.Hash())
|
||||
}
|
||||
for len(elements) > 0 {
|
||||
results := make([]NodeSyncResult, len(elements))
|
||||
if !bypath {
|
||||
for i, element := range elements {
|
||||
owner, inner := ResolvePath([]byte(element.path))
|
||||
data, err := srcDb.Reader(srcTrie.Hash()).Node(owner, inner, element.hash)
|
||||
data, err := reader.Node(owner, inner, element.hash)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve node data for hash %x: %v", element.hash, err)
|
||||
}
|
||||
@@ -230,12 +234,16 @@ func testIterativeDelayedSync(t *testing.T, scheme string) {
|
||||
syncPath: NewSyncPath([]byte(paths[i])),
|
||||
})
|
||||
}
|
||||
reader, err := srcDb.Reader(srcTrie.Hash())
|
||||
if err != nil {
|
||||
t.Fatalf("State is not available %x", srcTrie.Hash())
|
||||
}
|
||||
for len(elements) > 0 {
|
||||
// Sync only half of the scheduled nodes
|
||||
results := make([]NodeSyncResult, len(elements)/2+1)
|
||||
for i, element := range elements[:len(results)] {
|
||||
owner, inner := ResolvePath([]byte(element.path))
|
||||
data, err := srcDb.Reader(srcTrie.Hash()).Node(owner, inner, element.hash)
|
||||
data, err := reader.Node(owner, inner, element.hash)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve node data for %x: %v", element.hash, err)
|
||||
}
|
||||
@@ -295,12 +303,16 @@ func testIterativeRandomSync(t *testing.T, count int, scheme string) {
|
||||
syncPath: NewSyncPath([]byte(paths[i])),
|
||||
}
|
||||
}
|
||||
reader, err := srcDb.Reader(srcTrie.Hash())
|
||||
if err != nil {
|
||||
t.Fatalf("State is not available %x", srcTrie.Hash())
|
||||
}
|
||||
for len(queue) > 0 {
|
||||
// Fetch all the queued nodes in a random order
|
||||
results := make([]NodeSyncResult, 0, len(queue))
|
||||
for path, element := range queue {
|
||||
owner, inner := ResolvePath([]byte(element.path))
|
||||
data, err := srcDb.Reader(srcTrie.Hash()).Node(owner, inner, element.hash)
|
||||
data, err := reader.Node(owner, inner, element.hash)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve node data for %x: %v", element.hash, err)
|
||||
}
|
||||
@@ -358,12 +370,16 @@ func testIterativeRandomDelayedSync(t *testing.T, scheme string) {
|
||||
syncPath: NewSyncPath([]byte(path)),
|
||||
}
|
||||
}
|
||||
reader, err := srcDb.Reader(srcTrie.Hash())
|
||||
if err != nil {
|
||||
t.Fatalf("State is not available %x", srcTrie.Hash())
|
||||
}
|
||||
for len(queue) > 0 {
|
||||
// Sync only half of the scheduled nodes, even those in random order
|
||||
results := make([]NodeSyncResult, 0, len(queue)/2+1)
|
||||
for path, element := range queue {
|
||||
owner, inner := ResolvePath([]byte(element.path))
|
||||
data, err := srcDb.Reader(srcTrie.Hash()).Node(owner, inner, element.hash)
|
||||
data, err := reader.Node(owner, inner, element.hash)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve node data for %x: %v", element.hash, err)
|
||||
}
|
||||
@@ -426,13 +442,16 @@ func testDuplicateAvoidanceSync(t *testing.T, scheme string) {
|
||||
syncPath: NewSyncPath([]byte(paths[i])),
|
||||
})
|
||||
}
|
||||
reader, err := srcDb.Reader(srcTrie.Hash())
|
||||
if err != nil {
|
||||
t.Fatalf("State is not available %x", srcTrie.Hash())
|
||||
}
|
||||
requested := make(map[common.Hash]struct{})
|
||||
|
||||
for len(elements) > 0 {
|
||||
results := make([]NodeSyncResult, len(elements))
|
||||
for i, element := range elements {
|
||||
owner, inner := ResolvePath([]byte(element.path))
|
||||
data, err := srcDb.Reader(srcTrie.Hash()).Node(owner, inner, element.hash)
|
||||
data, err := reader.Node(owner, inner, element.hash)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve node data for %x: %v", element.hash, err)
|
||||
}
|
||||
@@ -501,12 +520,16 @@ func testIncompleteSync(t *testing.T, scheme string) {
|
||||
syncPath: NewSyncPath([]byte(paths[i])),
|
||||
})
|
||||
}
|
||||
reader, err := srcDb.Reader(srcTrie.Hash())
|
||||
if err != nil {
|
||||
t.Fatalf("State is not available %x", srcTrie.Hash())
|
||||
}
|
||||
for len(elements) > 0 {
|
||||
// Fetch a batch of trie nodes
|
||||
results := make([]NodeSyncResult, len(elements))
|
||||
for i, element := range elements {
|
||||
owner, inner := ResolvePath([]byte(element.path))
|
||||
data, err := srcDb.Reader(srcTrie.Hash()).Node(owner, inner, element.hash)
|
||||
data, err := reader.Node(owner, inner, element.hash)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve node data for %x: %v", element.hash, err)
|
||||
}
|
||||
@@ -585,12 +608,15 @@ func testSyncOrdering(t *testing.T, scheme string) {
|
||||
})
|
||||
reqs = append(reqs, NewSyncPath([]byte(paths[i])))
|
||||
}
|
||||
|
||||
reader, err := srcDb.Reader(srcTrie.Hash())
|
||||
if err != nil {
|
||||
t.Fatalf("State is not available %x", srcTrie.Hash())
|
||||
}
|
||||
for len(elements) > 0 {
|
||||
results := make([]NodeSyncResult, len(elements))
|
||||
for i, element := range elements {
|
||||
owner, inner := ResolvePath([]byte(element.path))
|
||||
data, err := srcDb.Reader(srcTrie.Hash()).Node(owner, inner, element.hash)
|
||||
data, err := reader.Node(owner, inner, element.hash)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve node data for %x: %v", element.hash, err)
|
||||
}
|
||||
@@ -649,11 +675,15 @@ func syncWith(t *testing.T, root common.Hash, db ethdb.Database, srcDb *Database
|
||||
syncPath: NewSyncPath([]byte(paths[i])),
|
||||
})
|
||||
}
|
||||
reader, err := srcDb.Reader(root)
|
||||
if err != nil {
|
||||
t.Fatalf("State is not available %x", root)
|
||||
}
|
||||
for len(elements) > 0 {
|
||||
results := make([]NodeSyncResult, len(elements))
|
||||
for i, element := range elements {
|
||||
owner, inner := ResolvePath([]byte(element.path))
|
||||
data, err := srcDb.Reader(root).Node(owner, inner, element.hash)
|
||||
data, err := reader.Node(owner, inner, element.hash)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve node data for hash %x: %v", element.hash, err)
|
||||
}
|
||||
|
||||
+4
-4
@@ -226,14 +226,14 @@ func TestAccessListLeak(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
func(tr *Trie) {
|
||||
it := tr.NodeIterator(nil)
|
||||
it := tr.MustNodeIterator(nil)
|
||||
for it.Next(true) {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
func(tr *Trie) {
|
||||
it := NewIterator(tr.NodeIterator(nil))
|
||||
it := NewIterator(tr.MustNodeIterator(nil))
|
||||
for it.Next() {
|
||||
}
|
||||
},
|
||||
@@ -300,7 +300,7 @@ func compareSet(setA, setB map[string]struct{}) bool {
|
||||
|
||||
func forNodes(tr *Trie) map[string][]byte {
|
||||
var (
|
||||
it = tr.NodeIterator(nil)
|
||||
it = tr.MustNodeIterator(nil)
|
||||
nodes = make(map[string][]byte)
|
||||
)
|
||||
for it.Next(true) {
|
||||
@@ -319,7 +319,7 @@ func iterNodes(db *Database, root common.Hash) map[string][]byte {
|
||||
|
||||
func forHashedNodes(tr *Trie) map[string][]byte {
|
||||
var (
|
||||
it = tr.NodeIterator(nil)
|
||||
it = tr.MustNodeIterator(nil)
|
||||
nodes = make(map[string][]byte)
|
||||
)
|
||||
for it.Next(true) {
|
||||
|
||||
+47
-9
@@ -39,6 +39,10 @@ type Trie struct {
|
||||
root node
|
||||
owner common.Hash
|
||||
|
||||
// Flag whether the commit operation is already performed. If so the
|
||||
// trie is not usable(latest states is invisible).
|
||||
committed bool
|
||||
|
||||
// Keep track of the number leaves which have been inserted since the last
|
||||
// hashing operation. This number will not directly map to the number of
|
||||
// actually unhashed nodes.
|
||||
@@ -60,11 +64,12 @@ func (t *Trie) newFlag() nodeFlag {
|
||||
// Copy returns a copy of Trie.
|
||||
func (t *Trie) Copy() *Trie {
|
||||
return &Trie{
|
||||
root: t.root,
|
||||
owner: t.owner,
|
||||
unhashed: t.unhashed,
|
||||
reader: t.reader,
|
||||
tracer: t.tracer.copy(),
|
||||
root: t.root,
|
||||
owner: t.owner,
|
||||
committed: t.committed,
|
||||
unhashed: t.unhashed,
|
||||
reader: t.reader,
|
||||
tracer: t.tracer.copy(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +79,7 @@ func (t *Trie) Copy() *Trie {
|
||||
// zero hash or the sha3 hash of an empty string, then trie is initially
|
||||
// empty, otherwise, the root node must be present in database or returns
|
||||
// a MissingNodeError if not.
|
||||
func New(id *ID, db NodeReader) (*Trie, error) {
|
||||
func New(id *ID, db *Database) (*Trie, error) {
|
||||
reader, err := newTrieReader(id.StateRoot, id.Owner, db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -100,10 +105,24 @@ func NewEmpty(db *Database) *Trie {
|
||||
return tr
|
||||
}
|
||||
|
||||
// MustNodeIterator is a wrapper of NodeIterator and will omit any encountered
|
||||
// error but just print out an error message.
|
||||
func (t *Trie) MustNodeIterator(start []byte) NodeIterator {
|
||||
it, err := t.NodeIterator(start)
|
||||
if err != nil {
|
||||
log.Error("Unhandled trie error in Trie.NodeIterator", "err", err)
|
||||
}
|
||||
return it
|
||||
}
|
||||
|
||||
// NodeIterator returns an iterator that returns nodes of the trie. Iteration starts at
|
||||
// the key after the given start key.
|
||||
func (t *Trie) NodeIterator(start []byte) NodeIterator {
|
||||
return newNodeIterator(t, start)
|
||||
func (t *Trie) NodeIterator(start []byte) (NodeIterator, error) {
|
||||
// Short circuit if the trie is already committed and not usable.
|
||||
if t.committed {
|
||||
return nil, ErrCommitted
|
||||
}
|
||||
return newNodeIterator(t, start), nil
|
||||
}
|
||||
|
||||
// MustGet is a wrapper of Get and will omit any encountered error but just
|
||||
@@ -122,6 +141,10 @@ func (t *Trie) MustGet(key []byte) []byte {
|
||||
// If the requested node is not present in trie, no error will be returned.
|
||||
// If the trie is corrupted, a MissingNodeError is returned.
|
||||
func (t *Trie) Get(key []byte) ([]byte, error) {
|
||||
// Short circuit if the trie is already committed and not usable.
|
||||
if t.committed {
|
||||
return nil, ErrCommitted
|
||||
}
|
||||
value, newroot, didResolve, err := t.get(t.root, keybytesToHex(key), 0)
|
||||
if err == nil && didResolve {
|
||||
t.root = newroot
|
||||
@@ -181,6 +204,10 @@ func (t *Trie) MustGetNode(path []byte) ([]byte, int) {
|
||||
// If the requested node is not present in trie, no error will be returned.
|
||||
// If the trie is corrupted, a MissingNodeError is returned.
|
||||
func (t *Trie) GetNode(path []byte) ([]byte, int, error) {
|
||||
// Short circuit if the trie is already committed and not usable.
|
||||
if t.committed {
|
||||
return nil, 0, ErrCommitted
|
||||
}
|
||||
item, newroot, resolved, err := t.getNode(t.root, compactToHex(path), 0)
|
||||
if err != nil {
|
||||
return nil, resolved, err
|
||||
@@ -273,6 +300,10 @@ func (t *Trie) MustUpdate(key, value []byte) {
|
||||
// If the requested node is not present in trie, no error will be returned.
|
||||
// If the trie is corrupted, a MissingNodeError is returned.
|
||||
func (t *Trie) Update(key, value []byte) error {
|
||||
// Short circuit if the trie is already committed and not usable.
|
||||
if t.committed {
|
||||
return ErrCommitted
|
||||
}
|
||||
return t.update(key, value)
|
||||
}
|
||||
|
||||
@@ -387,6 +418,10 @@ func (t *Trie) MustDelete(key []byte) {
|
||||
// If the requested node is not present in trie, no error will be returned.
|
||||
// If the trie is corrupted, a MissingNodeError is returned.
|
||||
func (t *Trie) Delete(key []byte) error {
|
||||
// Short circuit if the trie is already committed and not usable.
|
||||
if t.committed {
|
||||
return ErrCommitted
|
||||
}
|
||||
t.unhashed++
|
||||
k := keybytesToHex(key)
|
||||
_, n, err := t.delete(t.root, nil, k)
|
||||
@@ -574,7 +609,9 @@ func (t *Trie) Hash() common.Hash {
|
||||
// be created with new root and updated trie database for following usage
|
||||
func (t *Trie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet) {
|
||||
defer t.tracer.reset()
|
||||
|
||||
defer func() {
|
||||
t.committed = true
|
||||
}()
|
||||
nodes := trienode.NewNodeSet(t.owner)
|
||||
t.tracer.markDeletions(nodes)
|
||||
|
||||
@@ -621,4 +658,5 @@ func (t *Trie) Reset() {
|
||||
t.owner = common.Hash{}
|
||||
t.unhashed = 0
|
||||
t.tracer.reset()
|
||||
t.committed = false
|
||||
}
|
||||
|
||||
+12
-13
@@ -17,9 +17,9 @@
|
||||
package trie
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
// Reader wraps the Node method of a backing trie store.
|
||||
@@ -30,13 +30,6 @@ type Reader interface {
|
||||
Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error)
|
||||
}
|
||||
|
||||
// NodeReader wraps all the necessary functions for accessing trie node.
|
||||
type NodeReader interface {
|
||||
// Reader returns a reader for accessing all trie nodes with provided
|
||||
// state root. Nil is returned in case the state is not available.
|
||||
Reader(root common.Hash) Reader
|
||||
}
|
||||
|
||||
// trieReader is a wrapper of the underlying node reader. It's not safe
|
||||
// for concurrent usage.
|
||||
type trieReader struct {
|
||||
@@ -46,10 +39,16 @@ type trieReader struct {
|
||||
}
|
||||
|
||||
// newTrieReader initializes the trie reader with the given node reader.
|
||||
func newTrieReader(stateRoot, owner common.Hash, db NodeReader) (*trieReader, error) {
|
||||
reader := db.Reader(stateRoot)
|
||||
if reader == nil {
|
||||
return nil, fmt.Errorf("state not found #%x", stateRoot)
|
||||
func newTrieReader(stateRoot, owner common.Hash, db *Database) (*trieReader, error) {
|
||||
if stateRoot == (common.Hash{}) || stateRoot == types.EmptyRootHash {
|
||||
if stateRoot == (common.Hash{}) {
|
||||
log.Error("Zero state root hash!")
|
||||
}
|
||||
return &trieReader{owner: owner}, nil
|
||||
}
|
||||
reader, err := db.Reader(stateRoot)
|
||||
if err != nil {
|
||||
return nil, &MissingNodeError{Owner: owner, NodeHash: stateRoot, err: err}
|
||||
}
|
||||
return &trieReader{owner: owner, reader: reader}, nil
|
||||
}
|
||||
|
||||
+4
-4
@@ -521,7 +521,7 @@ func runRandTest(rt randTest) bool {
|
||||
origin = root
|
||||
case opItercheckhash:
|
||||
checktr := NewEmpty(triedb)
|
||||
it := NewIterator(tr.NodeIterator(nil))
|
||||
it := NewIterator(tr.MustNodeIterator(nil))
|
||||
for it.Next() {
|
||||
checktr.MustUpdate(it.Key, it.Value)
|
||||
}
|
||||
@@ -530,8 +530,8 @@ func runRandTest(rt randTest) bool {
|
||||
}
|
||||
case opNodeDiff:
|
||||
var (
|
||||
origIter = origTrie.NodeIterator(nil)
|
||||
curIter = tr.NodeIterator(nil)
|
||||
origIter = origTrie.MustNodeIterator(nil)
|
||||
curIter = tr.MustNodeIterator(nil)
|
||||
origSeen = make(map[string]struct{})
|
||||
curSeen = make(map[string]struct{})
|
||||
)
|
||||
@@ -710,7 +710,7 @@ func TestTinyTrie(t *testing.T) {
|
||||
t.Errorf("3: got %x, exp %x", root, exp)
|
||||
}
|
||||
checktr := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
it := NewIterator(trie.NodeIterator(nil))
|
||||
it := NewIterator(trie.MustNodeIterator(nil))
|
||||
for it.Next() {
|
||||
checktr.MustUpdate(it.Key, it.Value)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package hashdb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -621,8 +622,12 @@ func (db *Database) Scheme() string {
|
||||
}
|
||||
|
||||
// Reader retrieves a node reader belonging to the given state root.
|
||||
func (db *Database) Reader(root common.Hash) *reader {
|
||||
return &reader{db: db}
|
||||
// An error will be returned if the requested state is not available.
|
||||
func (db *Database) Reader(root common.Hash) (*reader, error) {
|
||||
if _, err := db.Node(root); err != nil {
|
||||
return nil, fmt.Errorf("state %#x is not available, %v", root, err)
|
||||
}
|
||||
return &reader{db: db}, nil
|
||||
}
|
||||
|
||||
// reader is a state reader of Database which implements the Reader interface.
|
||||
|
||||
Reference in New Issue
Block a user