diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index fb85794b76..5633198d39 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -19,6 +19,7 @@ package t8ntool import ( "fmt" "math/big" + "os" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" @@ -268,6 +269,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, // Commit block root, err := statedb.Commit(chainConfig.IsEIP158(vmContext.BlockNumber)) if err != nil { + fmt.Fprintf(os.Stderr, "Could not commit state: %v", err) return nil, nil, NewError(ErrorEVM, fmt.Errorf("could not commit state: %v", err)) } execRs := &ExecutionResult{ @@ -286,12 +288,6 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, h := types.DeriveSha(types.Withdrawals(pre.Env.Withdrawals), trie.NewStackTrie(nil)) execRs.WithdrawalsRoot = &h } - // Re-create statedb instance with new root upon the updated database - // for accessing latest states. - statedb, err = state.New(root, statedb.Database(), nil) - if err != nil { - return nil, nil, NewError(ErrorEVM, fmt.Errorf("could not reopen state: %v", err)) - } return statedb, execRs, nil } diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go index aa7939851d..b409b19260 100644 --- a/cmd/geth/dbcmd.go +++ b/cmd/geth/dbcmd.go @@ -519,12 +519,8 @@ func dbDumpTrie(ctx *cli.Context) error { if err != nil { return err } - trieIt, err := theTrie.NodeIterator(start) - if err != nil { - return err - } var count int64 - it := trie.NewIterator(trieIt) + it := trie.NewIterator(theTrie.NodeIterator(start)) for it.Next() { if max > 0 && count == max { fmt.Printf("Exiting after %d values\n", count) diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go index 67837261c6..7b6b939be3 100644 --- a/cmd/geth/snapshot.go +++ b/cmd/geth/snapshot.go @@ -292,12 +292,7 @@ func traverseState(ctx *cli.Context) error { lastReport time.Time start = time.Now() ) - acctIt, err := t.NodeIterator(nil) - if err != nil { - log.Error("Failed to open iterator", "root", root, "err", err) - return err - } - accIter := trie.NewIterator(acctIt) + accIter := trie.NewIterator(t.NodeIterator(nil)) for accIter.Next() { accounts += 1 var acc types.StateAccount @@ -312,12 +307,7 @@ func traverseState(ctx *cli.Context) error { log.Error("Failed to open storage trie", "root", acc.Root, "err", err) return err } - storageIt, err := storageTrie.NodeIterator(nil) - if err != nil { - log.Error("Failed to open storage iterator", "root", acc.Root, "err", err) - return err - } - storageIter := trie.NewIterator(storageIt) + storageIter := trie.NewIterator(storageTrie.NodeIterator(nil)) for storageIter.Next() { slots += 1 } @@ -395,11 +385,7 @@ func traverseRawState(ctx *cli.Context) error { hasher = crypto.NewKeccakState() got = make([]byte, 32) ) - accIter, err := t.NodeIterator(nil) - if err != nil { - log.Error("Failed to open iterator", "root", root, "err", err) - return err - } + accIter := t.NodeIterator(nil) for accIter.Next(true) { nodes += 1 node := accIter.Hash() @@ -436,11 +422,7 @@ func traverseRawState(ctx *cli.Context) error { log.Error("Failed to open storage trie", "root", acc.Root, "err", err) return errors.New("missing storage trie") } - storageIter, err := storageTrie.NodeIterator(nil) - if err != nil { - log.Error("Failed to open storage iterator", "root", acc.Root, "err", err) - return err - } + storageIter := storageTrie.NodeIterator(nil) for storageIter.Next(true) { nodes += 1 node := storageIter.Hash() diff --git a/core/state/database.go b/core/state/database.go index 2c8a7afee6..304c04f963 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -113,9 +113,8 @@ type Trie interface { Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet) // NodeIterator returns an iterator that returns nodes of the trie. Iteration - // starts at the key after the given start key. And error will be returned - // if fails to create node iterator. - NodeIterator(startKey []byte) (trie.NodeIterator, error) + // starts at the key after the given start key. + NodeIterator(startKey []byte) trie.NodeIterator // Prove constructs a Merkle proof for key. The result contains all encoded nodes // on the path to the value at key. The value itself is also included in the last diff --git a/core/state/dump.go b/core/state/dump.go index 2d9c65e473..96a2ac3d3e 100644 --- a/core/state/dump.go +++ b/core/state/dump.go @@ -140,11 +140,7 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey [] log.Info("Trie dumping started", "root", s.trie.Hash()) c.OnRoot(s.trie.Hash()) - trieIt, err := s.trie.NodeIterator(conf.Start) - if err != nil { - return nil - } - it := trie.NewIterator(trieIt) + it := trie.NewIterator(s.trie.NodeIterator(conf.Start)) for it.Next() { var data types.StateAccount if err := rlp.DecodeBytes(it.Value, &data); err != nil { @@ -182,12 +178,7 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey [] log.Error("Failed to load storage trie", "err", err) continue } - trieIt, err := tr.NodeIterator(nil) - if err != nil { - log.Error("Failed to create trie iterator", "err", err) - continue - } - storageIt := trie.NewIterator(trieIt) + storageIt := trie.NewIterator(tr.NodeIterator(nil)) for storageIt.Next() { _, content, _, err := rlp.Split(storageIt.Value) if err != nil { diff --git a/core/state/iterator.go b/core/state/iterator.go index 7802263b75..79bb650abc 100644 --- a/core/state/iterator.go +++ b/core/state/iterator.go @@ -74,12 +74,8 @@ func (it *nodeIterator) step() error { return nil } // Initialize the iterator if we've just started - var err error if it.stateIt == nil { - it.stateIt, err = it.state.trie.NodeIterator(nil) - if err != nil { - return err - } + it.stateIt = it.state.trie.NodeIterator(nil) } // If we had data nodes previously, we surely have at least state nodes if it.dataIt != nil { @@ -117,10 +113,7 @@ func (it *nodeIterator) step() error { if err != nil { return err } - it.dataIt, err = dataTrie.NodeIterator(nil) - if err != nil { - return err - } + it.dataIt = dataTrie.NodeIterator(nil) if !it.dataIt.Next(true) { it.dataIt = nil } diff --git a/core/state/pruner/pruner.go b/core/state/pruner/pruner.go index 1ee5a0e625..aed002f905 100644 --- a/core/state/pruner/pruner.go +++ b/core/state/pruner/pruner.go @@ -420,10 +420,7 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error { if err != nil { return err } - accIter, err := t.NodeIterator(nil) - if err != nil { - return err - } + accIter := t.NodeIterator(nil) for accIter.Next(true) { hash := accIter.Hash() @@ -444,10 +441,7 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error { if err != nil { return err } - storageIter, err := storageTrie.NodeIterator(nil) - if err != nil { - return err - } + storageIter := storageTrie.NodeIterator(nil) for storageIter.Next(true) { hash := storageIter.Hash() if hash != (common.Hash{}) { diff --git a/core/state/snapshot/generate.go b/core/state/snapshot/generate.go index 1ec82b9442..78aa23a5de 100644 --- a/core/state/snapshot/generate.go +++ b/core/state/snapshot/generate.go @@ -382,6 +382,8 @@ func (dl *diskLayer) generateRange(ctx *generatorContext, trieId *trie.ID, prefi } var ( trieMore bool + nodeIt = tr.NodeIterator(origin) + iter = trie.NewIterator(nodeIt) kvkeys, kvvals = result.keys, result.vals // counters @@ -395,12 +397,7 @@ func (dl *diskLayer) generateRange(ctx *generatorContext, trieId *trie.ID, prefi start = time.Now() internal time.Duration ) - nodeIt, err := tr.NodeIterator(origin) - if err != nil { - return false, nil, err - } nodeIt.AddResolver(resolver) - iter := trie.NewIterator(nodeIt) for iter.Next() { if last != nil && bytes.Compare(iter.Key, last) > 0 { diff --git a/core/state/state_test.go b/core/state/state_test.go index 555e1f7454..15e6037265 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -43,8 +43,7 @@ func newStateTest() *stateTest { func TestDump(t *testing.T) { db := rawdb.NewMemoryDatabase() - tdb := NewDatabaseWithConfig(db, &trie.Config{Preimages: true}) - sdb, _ := New(types.EmptyRootHash, tdb, nil) + sdb, _ := New(types.EmptyRootHash, NewDatabaseWithConfig(db, &trie.Config{Preimages: true}), nil) s := &stateTest{db: db, state: sdb} // generate a few entries @@ -58,10 +57,9 @@ func TestDump(t *testing.T) { // write some of them to the trie s.state.updateStateObject(obj1) s.state.updateStateObject(obj2) - root, _ := s.state.Commit(false) + s.state.Commit(false) // check that DumpToCollector contains the state objects that are in trie - s.state, _ = New(root, tdb, nil) got := string(s.state.Dump(nil)) want := `{ "root": "71edff0130dd2385947095001c73d9e28d862fc286fca2b922ca6f6f3cddfdd2", @@ -97,8 +95,7 @@ func TestDump(t *testing.T) { func TestIterativeDump(t *testing.T) { db := rawdb.NewMemoryDatabase() - tdb := NewDatabaseWithConfig(db, &trie.Config{Preimages: true}) - sdb, _ := New(types.EmptyRootHash, tdb, nil) + sdb, _ := New(types.EmptyRootHash, NewDatabaseWithConfig(db, &trie.Config{Preimages: true}), nil) s := &stateTest{db: db, state: sdb} // generate a few entries @@ -114,8 +111,7 @@ func TestIterativeDump(t *testing.T) { // write some of them to the trie s.state.updateStateObject(obj1) s.state.updateStateObject(obj2) - root, _ := s.state.Commit(false) - s.state, _ = New(root, tdb, nil) + s.state.Commit(false) b := &bytes.Buffer{} s.state.IterativeDump(nil, json.NewEncoder(b)) diff --git a/core/state/statedb.go b/core/state/statedb.go index f8457c6de3..90b32e3fc0 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -56,14 +56,8 @@ func (n *proofList) Delete(key []byte) error { // StateDB structs within the ethereum protocol are used to store anything // within the merkle trie. StateDBs take care of caching and storing // nested states. It's the general query interface to retrieve: -// // * Contracts // * Accounts -// -// Once the state is committed, tries cached in stateDB (including account -// trie, storage tries) will no longer be functional. A new state instance -// must be created with new root and updated database for accessing post- -// commit states. type StateDB struct { db Database prefetcher *triePrefetcher @@ -686,23 +680,19 @@ func (s *StateDB) CreateAccount(addr common.Address) { } } -func (s *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error { - so := s.getStateObject(addr) +func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) error { + so := db.getStateObject(addr) if so == nil { return nil } - tr, err := so.getTrie(s.db) + tr, err := so.getTrie(db.db) if err != nil { return err } - trieIt, err := tr.NodeIterator(nil) - if err != nil { - return err - } - it := trie.NewIterator(trieIt) + it := trie.NewIterator(tr.NodeIterator(nil)) for it.Next() { - key := common.BytesToHash(s.trie.GetKey(it.Key)) + key := common.BytesToHash(db.trie.GetKey(it.Key)) if value, dirty := so.dirtyStorage[key]; dirty { if !cb(key, value) { return nil @@ -987,10 +977,6 @@ func (s *StateDB) clearJournalAndRefund() { } // Commit writes the state to the underlying in-memory trie database. -// Once the state is committed, tries cached in stateDB (including account -// trie, storage tries) will no longer be functional. A new state instance -// must be created with new root and updated database for accessing post- -// commit states. func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) { // Short circuit in case any database failure occurred earlier. if s.dbErr != nil { diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 44eace01d8..82118fc5f4 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -19,7 +19,6 @@ package state import ( "bytes" "encoding/binary" - "errors" "fmt" "math" "math/big" @@ -522,8 +521,7 @@ func TestCopyOfCopy(t *testing.T) { // // See https://github.com/ethereum/go-ethereum/issues/20106. func TestCopyCommitCopy(t *testing.T) { - tdb := NewDatabase(rawdb.NewMemoryDatabase()) - state, _ := New(types.EmptyRootHash, tdb, nil) + state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) // Create an account and check if the retrieved balance is correct addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe") @@ -560,6 +558,20 @@ func TestCopyCommitCopy(t *testing.T) { if val := copyOne.GetCommittedState(addr, skey); val != (common.Hash{}) { t.Fatalf("first copy pre-commit committed storage slot mismatch: have %x, want %x", val, common.Hash{}) } + + copyOne.Commit(false) + if balance := copyOne.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { + t.Fatalf("first copy post-commit balance mismatch: have %v, want %v", balance, 42) + } + if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) { + t.Fatalf("first copy post-commit code mismatch: have %x, want %x", code, []byte("hello")) + } + if val := copyOne.GetState(addr, skey); val != sval { + t.Fatalf("first copy post-commit non-committed storage slot mismatch: have %x, want %x", val, sval) + } + if val := copyOne.GetCommittedState(addr, skey); val != sval { + t.Fatalf("first copy post-commit committed storage slot mismatch: have %x, want %x", val, sval) + } // Copy the copy and check the balance once more copyTwo := copyOne.Copy() if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { @@ -571,23 +583,8 @@ func TestCopyCommitCopy(t *testing.T) { if val := copyTwo.GetState(addr, skey); val != sval { t.Fatalf("second copy non-committed storage slot mismatch: have %x, want %x", val, sval) } - if val := copyTwo.GetCommittedState(addr, skey); val != (common.Hash{}) { - t.Fatalf("second copy committed storage slot mismatch: have %x, want %x", val, sval) - } - // Commit state, ensure states can be loaded from disk - root, _ := state.Commit(false) - state, _ = New(root, tdb, nil) - if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { - t.Fatalf("state post-commit balance mismatch: have %v, want %v", balance, 42) - } - if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) { - t.Fatalf("state post-commit code mismatch: have %x, want %x", code, []byte("hello")) - } - if val := state.GetState(addr, skey); val != sval { - t.Fatalf("state post-commit non-committed storage slot mismatch: have %x, want %x", val, sval) - } - if val := state.GetCommittedState(addr, skey); val != sval { - t.Fatalf("state post-commit committed storage slot mismatch: have %x, want %x", val, sval) + if val := copyTwo.GetCommittedState(addr, skey); val != sval { + t.Fatalf("second copy post-commit committed storage slot mismatch: have %x, want %x", val, sval) } } @@ -647,6 +644,19 @@ func TestCopyCopyCommitCopy(t *testing.T) { if val := copyTwo.GetCommittedState(addr, skey); val != (common.Hash{}) { t.Fatalf("second copy pre-commit committed storage slot mismatch: have %x, want %x", val, common.Hash{}) } + copyTwo.Commit(false) + if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { + t.Fatalf("second copy post-commit balance mismatch: have %v, want %v", balance, 42) + } + if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) { + t.Fatalf("second copy post-commit code mismatch: have %x, want %x", code, []byte("hello")) + } + if val := copyTwo.GetState(addr, skey); val != sval { + t.Fatalf("second copy post-commit non-committed storage slot mismatch: have %x, want %x", val, sval) + } + if val := copyTwo.GetCommittedState(addr, skey); val != sval { + t.Fatalf("second copy post-commit committed storage slot mismatch: have %x, want %x", val, sval) + } // Copy the copy-copy and check the balance once more copyThree := copyTwo.Copy() if balance := copyThree.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { @@ -658,56 +668,11 @@ func TestCopyCopyCommitCopy(t *testing.T) { if val := copyThree.GetState(addr, skey); val != sval { t.Fatalf("third copy non-committed storage slot mismatch: have %x, want %x", val, sval) } - if val := copyThree.GetCommittedState(addr, skey); val != (common.Hash{}) { + if val := copyThree.GetCommittedState(addr, skey); val != sval { t.Fatalf("third copy committed storage slot mismatch: have %x, want %x", val, sval) } } -// TestCommitCopy tests the copy from a committed state is not functional. -func TestCommitCopy(t *testing.T) { - state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil) - - // Create an account and check if the retrieved balance is correct - addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe") - skey := common.HexToHash("aaa") - sval := common.HexToHash("bbb") - - state.SetBalance(addr, big.NewInt(42)) // Change the account trie - state.SetCode(addr, []byte("hello")) // Change an external metadata - state.SetState(addr, skey, sval) // Change the storage trie - - if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { - t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42) - } - if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) { - t.Fatalf("initial code mismatch: have %x, want %x", code, []byte("hello")) - } - if val := state.GetState(addr, skey); val != sval { - t.Fatalf("initial non-committed storage slot mismatch: have %x, want %x", val, sval) - } - if val := state.GetCommittedState(addr, skey); val != (common.Hash{}) { - t.Fatalf("initial committed storage slot mismatch: have %x, want %x", val, common.Hash{}) - } - // Copy the committed state database, the copied one is not functional. - state.Commit(true) - copied := state.Copy() - if balance := copied.GetBalance(addr); balance.Cmp(big.NewInt(0)) != 0 { - t.Fatalf("unexpected balance: have %v", balance) - } - if code := copied.GetCode(addr); code != nil { - t.Fatalf("unexpected code: have %x", code) - } - if val := copied.GetState(addr, skey); val != (common.Hash{}) { - t.Fatalf("unexpected storage slot: have %x", val) - } - if val := copied.GetCommittedState(addr, skey); val != (common.Hash{}) { - t.Fatalf("unexpected storage slot: have %x", val) - } - if !errors.Is(copied.Error(), trie.ErrCommitted) { - t.Fatalf("unexpected state error, %v", copied.Error()) - } -} - // TestDeleteCreateRevert tests a weird state transition corner case that we hit // while changing the internals of StateDB. The workflow is that a contract is // self-destructed, then in a follow-up transaction (but same block) it's created diff --git a/core/state/sync_test.go b/core/state/sync_test.go index cac0ee6e48..6e9d9342ee 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -109,7 +109,7 @@ func checkTrieConsistency(db ethdb.Database, root common.Hash) error { if err != nil { return err } - it := trie.MustNodeIterator(nil) + it := trie.NodeIterator(nil) for it.Next(true) { } return it.Error() @@ -566,10 +566,6 @@ func TestIncompleteStateSync(t *testing.T) { addedPaths []string addedHashes []common.Hash ) - reader, err := srcDb.TrieDB().Reader(srcRoot) - if err != nil { - t.Fatalf("state is not available %x", srcRoot) - } nodeQueue := make(map[string]stateElement) codeQueue := make(map[common.Hash]struct{}) paths, nodes, codes := sched.Missing(1) @@ -607,7 +603,7 @@ func TestIncompleteStateSync(t *testing.T) { results := make([]trie.NodeSyncResult, 0, len(nodeQueue)) for path, element := range nodeQueue { owner, inner := trie.ResolvePath([]byte(element.path)) - data, err := reader.Node(owner, inner, element.hash) + data, err := srcDb.TrieDB().Reader(srcRoot).Node(owner, inner, element.hash) if err != nil { t.Fatalf("failed to retrieve node data for %x", element.hash) } diff --git a/eth/api_debug.go b/eth/api_debug.go index c25e024cb2..6f9daadd6b 100644 --- a/eth/api_debug.go +++ b/eth/api_debug.go @@ -235,11 +235,7 @@ func (api *DebugAPI) StorageRangeAt(ctx context.Context, blockNrOrHash rpc.Block } func storageRangeAt(st state.Trie, start []byte, maxResult int) (StorageRangeResult, error) { - trieIt, err := st.NodeIterator(start) - if err != nil { - return StorageRangeResult{}, err - } - it := trie.NewIterator(trieIt) + it := trie.NewIterator(st.NodeIterator(start)) result := StorageRangeResult{Storage: storageMap{}} for i := 0; i < maxResult && it.Next(); i++ { _, content, _, err := rlp.Split(it.Value) @@ -330,15 +326,7 @@ func (api *DebugAPI) getModifiedAccounts(startBlock, endBlock *types.Block) ([]c if err != nil { return nil, err } - oldIt, err := oldTrie.NodeIterator([]byte{}) - if err != nil { - return nil, err - } - newIt, err := newTrie.NodeIterator([]byte{}) - if err != nil { - return nil, err - } - diff, _ := trie.NewDifferenceIterator(oldIt, newIt) + diff, _ := trie.NewDifferenceIterator(oldTrie.NodeIterator([]byte{}), newTrie.NodeIterator([]byte{})) iter := trie.NewIterator(diff) var dirty []common.Address diff --git a/eth/api_debug_test.go b/eth/api_debug_test.go index ee54e4c3c8..c7f0d4ac90 100644 --- a/eth/api_debug_test.go +++ b/eth/api_debug_test.go @@ -62,34 +62,34 @@ func TestAccountRange(t *testing.T) { t.Parallel() var ( - statedb = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &trie.Config{Preimages: true}) - sdb, _ = state.New(types.EmptyRootHash, statedb, nil) - addrs = [AccountRangeMaxResults * 2]common.Address{} - m = map[common.Address]bool{} + statedb = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &trie.Config{Preimages: true}) + state, _ = state.New(types.EmptyRootHash, statedb, nil) + addrs = [AccountRangeMaxResults * 2]common.Address{} + m = map[common.Address]bool{} ) for i := range addrs { hash := common.HexToHash(fmt.Sprintf("%x", i)) addr := common.BytesToAddress(crypto.Keccak256Hash(hash.Bytes()).Bytes()) addrs[i] = addr - sdb.SetBalance(addrs[i], big.NewInt(1)) + state.SetBalance(addrs[i], big.NewInt(1)) if _, ok := m[addr]; ok { t.Fatalf("bad") } else { m[addr] = true } } - root, _ := sdb.Commit(true) - sdb, _ = state.New(root, statedb, nil) + state.Commit(true) + root := state.IntermediateRoot(true) trie, err := statedb.OpenTrie(root) if err != nil { t.Fatal(err) } - accountRangeTest(t, &trie, sdb, common.Hash{}, AccountRangeMaxResults/2, AccountRangeMaxResults/2) + accountRangeTest(t, &trie, state, common.Hash{}, AccountRangeMaxResults/2, AccountRangeMaxResults/2) // test pagination - firstResult := accountRangeTest(t, &trie, sdb, common.Hash{}, AccountRangeMaxResults, AccountRangeMaxResults) - secondResult := accountRangeTest(t, &trie, sdb, common.BytesToHash(firstResult.Next), AccountRangeMaxResults, AccountRangeMaxResults) + firstResult := accountRangeTest(t, &trie, state, common.Hash{}, AccountRangeMaxResults, AccountRangeMaxResults) + secondResult := accountRangeTest(t, &trie, state, common.BytesToHash(firstResult.Next), AccountRangeMaxResults, AccountRangeMaxResults) hList := make([]common.Hash, 0) for addr1 := range firstResult.Accounts { @@ -107,7 +107,7 @@ func TestAccountRange(t *testing.T) { // set and get an even split between the first and second sets. slices.SortFunc(hList, common.Hash.Less) middleH := hList[AccountRangeMaxResults/2] - middleResult := accountRangeTest(t, &trie, sdb, middleH, AccountRangeMaxResults, AccountRangeMaxResults) + middleResult := accountRangeTest(t, &trie, state, middleH, AccountRangeMaxResults, AccountRangeMaxResults) missing, infirst, insecond := 0, 0, 0 for h := range middleResult.Accounts { if _, ok := firstResult.Accounts[h]; ok { @@ -136,10 +136,8 @@ func TestEmptyAccountRange(t *testing.T) { statedb = state.NewDatabase(rawdb.NewMemoryDatabase()) st, _ = state.New(types.EmptyRootHash, statedb, nil) ) - // Commit(although nothing to flush) and re-init the statedb st.Commit(true) - st, _ = state.New(types.EmptyRootHash, statedb, nil) - + st.IntermediateRoot(true) results := st.IteratorDump(&state.DumpConfig{ SkipCode: true, SkipStorage: true, diff --git a/eth/protocols/snap/sync_test.go b/eth/protocols/snap/sync_test.go index 173f1da604..6efe0c8325 100644 --- a/eth/protocols/snap/sync_test.go +++ b/eth/protocols/snap/sync_test.go @@ -1664,7 +1664,7 @@ func verifyTrie(db ethdb.KeyValueStore, root common.Hash, t *testing.T) { t.Fatal(err) } accounts, slots := 0, 0 - accIt := trie.NewIterator(accTrie.MustNodeIterator(nil)) + accIt := trie.NewIterator(accTrie.NodeIterator(nil)) for accIt.Next() { var acc struct { Nonce uint64 @@ -1682,7 +1682,7 @@ func verifyTrie(db ethdb.KeyValueStore, root common.Hash, t *testing.T) { if err != nil { t.Fatal(err) } - storeIt := trie.NewIterator(storeTrie.MustNodeIterator(nil)) + storeIt := trie.NewIterator(storeTrie.NodeIterator(nil)) for storeIt.Next() { slots++ } diff --git a/light/trie.go b/light/trie.go index e0675f98c9..888598ca92 100644 --- a/light/trie.go +++ b/light/trie.go @@ -184,8 +184,8 @@ func (t *odrTrie) Hash() common.Hash { return t.trie.Hash() } -func (t *odrTrie) NodeIterator(startkey []byte) (trie.NodeIterator, error) { - return newNodeIterator(t, startkey), nil +func (t *odrTrie) NodeIterator(startkey []byte) trie.NodeIterator { + return newNodeIterator(t, startkey) } func (t *odrTrie) GetKey(sha []byte) []byte { @@ -248,11 +248,7 @@ func newNodeIterator(t *odrTrie, startkey []byte) trie.NodeIterator { }) } it.do(func() error { - var err error - it.NodeIterator, err = it.t.trie.NodeIterator(startkey) - if err != nil { - return err - } + it.NodeIterator = it.t.trie.NodeIterator(startkey) return it.NodeIterator.Error() }) return it diff --git a/light/trie_test.go b/light/trie_test.go index ad7d769c84..0a24609cbd 100644 --- a/light/trie_test.go +++ b/light/trie_test.go @@ -62,16 +62,8 @@ func TestNodeIterator(t *testing.T) { } func diffTries(t1, t2 state.Trie) error { - trieIt1, err := t1.NodeIterator(nil) - if err != nil { - return err - } - trieIt2, err := t2.NodeIterator(nil) - if err != nil { - return err - } - i1 := trie.NewIterator(trieIt1) - i2 := trie.NewIterator(trieIt2) + i1 := trie.NewIterator(t1.NodeIterator(nil)) + i2 := trie.NewIterator(t2.NodeIterator(nil)) for i1.Next() && i2.Next() { if !bytes.Equal(i1.Key, i2.Key) { spew.Dump(i2) diff --git a/tests/fuzzers/stacktrie/trie_fuzzer.go b/tests/fuzzers/stacktrie/trie_fuzzer.go index d7be54eda0..77e602f807 100644 --- a/tests/fuzzers/stacktrie/trie_fuzzer.go +++ b/tests/fuzzers/stacktrie/trie_fuzzer.go @@ -221,7 +221,7 @@ func (f *fuzzer) fuzz() int { panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootC)) } trieA, _ = trie.New(trie.TrieID(rootA), dbA) - iterA := trieA.MustNodeIterator(nil) + iterA := trieA.NodeIterator(nil) for iterA.Next(true) { if iterA.Hash() == (common.Hash{}) { if _, present := nodeset[string(iterA.Path())]; present { diff --git a/tests/fuzzers/trie/trie-fuzzer.go b/tests/fuzzers/trie/trie-fuzzer.go index 33edc2a18a..0926d04764 100644 --- a/tests/fuzzers/trie/trie-fuzzer.go +++ b/tests/fuzzers/trie/trie-fuzzer.go @@ -179,7 +179,7 @@ func runRandTest(rt randTest) error { origin = hash case opItercheckhash: checktr := trie.NewEmpty(triedb) - it := trie.NewIterator(tr.MustNodeIterator(nil)) + it := trie.NewIterator(tr.NodeIterator(nil)) for it.Next() { checktr.MustUpdate(it.Key, it.Value) } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 667e951b66..eec91b67a7 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -204,11 +204,6 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config, snapshotter bo if logs := rlpHash(statedb.Logs()); logs != common.Hash(post.Logs) { return snaps, statedb, fmt.Errorf("post state logs hash mismatch: got %x, want %x", logs, post.Logs) } - // Re-init the post-state instance for further operation - statedb, err = state.New(root, statedb.Database(), snaps) - if err != nil { - return nil, nil, err - } return snaps, statedb, nil } @@ -280,7 +275,9 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh // the coinbase gets no txfee, so isn't created, and thus needs to be touched statedb.AddBalance(block.Coinbase(), new(big.Int)) // Commit block - root, _ := statedb.Commit(config.IsEIP158(block.Number())) + statedb.Commit(config.IsEIP158(block.Number())) + // And _now_ get the state root + root := statedb.IntermediateRoot(config.IsEIP158(block.Number())) return snaps, statedb, root, err } diff --git a/trie/database_wrap.go b/trie/database_wrap.go index 30835b7d25..b05f56847a 100644 --- a/trie/database_wrap.go +++ b/trie/database_wrap.go @@ -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. -// An error will be returned if the requested state is not available. -func (db *Database) Reader(blockRoot common.Hash) (Reader, error) { +// Nil is returned in case the state is not available. +func (db *Database) Reader(blockRoot common.Hash) Reader { return db.backend.(*hashdb.Database).Reader(blockRoot) } diff --git a/trie/errors.go b/trie/errors.go index 7be7041c7f..bd82b950aa 100644 --- a/trie/errors.go +++ b/trie/errors.go @@ -17,17 +17,11 @@ 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. diff --git a/trie/iterator_test.go b/trie/iterator_test.go index 5c7f9c98fb..ccf5ea0258 100644 --- a/trie/iterator_test.go +++ b/trie/iterator_test.go @@ -34,7 +34,7 @@ import ( func TestEmptyIterator(t *testing.T) { trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase())) - iter := trie.MustNodeIterator(nil) + iter := trie.NodeIterator(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.MustNodeIterator(nil)) + it := NewIterator(trie.NodeIterator(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.MustNodeIterator(nil)) + it := NewIterator(trie.NodeIterator(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.MustNodeIterator(nil); it.Next(true); { + for it := trie.NodeIterator(nil); it.Next(true); { if it.Hash() != (common.Hash{}) { elements[it.Hash()] = iterationElement{ hash: it.Hash(), @@ -149,12 +149,8 @@ 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 := reader.Node(common.Hash{}, element.path, element.hash); err != nil { + if blob, err := nodeDb.Reader(trie.Hash()).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) @@ -214,19 +210,19 @@ func TestIteratorSeek(t *testing.T) { } // Seek to the middle. - it := NewIterator(trie.MustNodeIterator([]byte("fab"))) + it := NewIterator(trie.NodeIterator([]byte("fab"))) if err := checkIteratorOrder(testdata1[4:], it); err != nil { t.Fatal(err) } // Seek to a non-existent key. - it = NewIterator(trie.MustNodeIterator([]byte("barc"))) + it = NewIterator(trie.NodeIterator([]byte("barc"))) if err := checkIteratorOrder(testdata1[1:], it); err != nil { t.Fatal(err) } // Seek beyond the end. - it = NewIterator(trie.MustNodeIterator([]byte("z"))) + it = NewIterator(trie.NodeIterator([]byte("z"))) if err := checkIteratorOrder(nil, it); err != nil { t.Fatal(err) } @@ -268,7 +264,7 @@ func TestDifferenceIterator(t *testing.T) { trieb, _ = New(TrieID(rootB), dbb) found := make(map[string]string) - di, _ := NewDifferenceIterator(triea.MustNodeIterator(nil), trieb.MustNodeIterator(nil)) + di, _ := NewDifferenceIterator(triea.NodeIterator(nil), trieb.NodeIterator(nil)) it := NewIterator(di) for it.Next() { found[string(it.Key)] = string(it.Value) @@ -309,7 +305,7 @@ func TestUnionIterator(t *testing.T) { dbb.Update(rootB, types.EmptyRootHash, trienode.NewWithNodeSet(nodesB)) trieb, _ = New(TrieID(rootB), dbb) - di, _ := NewUnionIterator([]NodeIterator{triea.MustNodeIterator(nil), trieb.MustNodeIterator(nil)}) + di, _ := NewUnionIterator([]NodeIterator{triea.NodeIterator(nil), trieb.NodeIterator(nil)}) it := NewIterator(di) all := []struct{ k, v string }{ @@ -348,7 +344,7 @@ func TestIteratorNoDups(t *testing.T) { for _, val := range testdata1 { tr.MustUpdate([]byte(val.k), []byte(val.v)) } - checkIteratorNoDups(t, tr.MustNodeIterator(nil), nil) + checkIteratorNoDups(t, tr.NodeIterator(nil), nil) } // This test checks that nodeIterator.Next can be retried after inserting missing trie nodes. @@ -373,7 +369,7 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool, scheme string) { tdb.Commit(root, false) } tr, _ = New(TrieID(root), tdb) - wantNodeCount := checkIteratorNoDups(t, tr.MustNodeIterator(nil), nil) + wantNodeCount := checkIteratorNoDups(t, tr.NodeIterator(nil), nil) var ( paths [][]byte @@ -432,7 +428,7 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool, scheme string) { } // Iterate until the error is hit. seen := make(map[string]bool) - it := tr.MustNodeIterator(nil) + it := tr.NodeIterator(nil) checkIteratorNoDups(t, it, seen) missing, ok := it.Error().(*MissingNodeError) if !ok || missing.NodeHash != rhash { @@ -500,7 +496,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.MustNodeIterator([]byte("bars")) + it := tr.NodeIterator([]byte("bars")) missing, ok := it.Error().(*MissingNodeError) if !ok { t.Fatal("want MissingNodeError, got", it.Error()) @@ -606,10 +602,7 @@ 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 } @@ -621,8 +614,8 @@ func TestNodeIteratorLargeTrie(t *testing.T) { // Do a seek operation trie.NodeIterator(common.FromHex("0x77667766776677766778855885885885")) // master: 24 get operations - // this pr: 6 get operations - if have, want := logDb.getCount, uint64(6); have != want { + // this pr: 5 get operations + if have, want := logDb.getCount, uint64(5); have != want { t.Fatalf("Too many lookups during seek, have %d want %d", have, want) } } @@ -653,7 +646,7 @@ func testIteratorNodeBlob(t *testing.T, scheme string) { var found = make(map[common.Hash][]byte) trie, _ = New(TrieID(root), triedb) - it := trie.MustNodeIterator(nil) + it := trie.NodeIterator(nil) for it.Next(true) { if it.Hash() == (common.Hash{}) { continue diff --git a/trie/proof.go b/trie/proof.go index a463c80b48..818e338892 100644 --- a/trie/proof.go +++ b/trie/proof.go @@ -34,10 +34,6 @@ 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 diff --git a/trie/proof_test.go b/trie/proof_test.go index d8effd0d04..6af2c3e472 100644 --- a/trie/proof_test.go +++ b/trie/proof_test.go @@ -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.MustNodeIterator(key)); it.Next() && bytes.Equal(key, it.Key) { + if it := NewIterator(trie.NodeIterator(key)); it.Next() && bytes.Equal(key, it.Key) { for _, p := range it.Prove() { proof.Put(crypto.Keccak256(p), p) } diff --git a/trie/secure_trie.go b/trie/secure_trie.go index a6d5676197..62666c7f0c 100644 --- a/trie/secure_trie.go +++ b/trie/secure_trie.go @@ -250,18 +250,12 @@ 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, error) { +// 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 { 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. diff --git a/trie/sync_test.go b/trie/sync_test.go index ec49beef04..7cd4058176 100644 --- a/trie/sync_test.go +++ b/trie/sync_test.go @@ -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.MustNodeIterator(nil) + it := trie.NodeIterator(nil) for it.Next(true) { } return it.Error() @@ -159,16 +159,12 @@ 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 := reader.Node(owner, inner, element.hash) + data, err := srcDb.Reader(srcTrie.Hash()).Node(owner, inner, element.hash) if err != nil { t.Fatalf("failed to retrieve node data for hash %x: %v", element.hash, err) } @@ -234,16 +230,12 @@ 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 := reader.Node(owner, inner, element.hash) + data, err := srcDb.Reader(srcTrie.Hash()).Node(owner, inner, element.hash) if err != nil { t.Fatalf("failed to retrieve node data for %x: %v", element.hash, err) } @@ -303,16 +295,12 @@ 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 := reader.Node(owner, inner, element.hash) + data, err := srcDb.Reader(srcTrie.Hash()).Node(owner, inner, element.hash) if err != nil { t.Fatalf("failed to retrieve node data for %x: %v", element.hash, err) } @@ -370,16 +358,12 @@ 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 := reader.Node(owner, inner, element.hash) + data, err := srcDb.Reader(srcTrie.Hash()).Node(owner, inner, element.hash) if err != nil { t.Fatalf("failed to retrieve node data for %x: %v", element.hash, err) } @@ -442,16 +426,13 @@ 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 := reader.Node(owner, inner, element.hash) + data, err := srcDb.Reader(srcTrie.Hash()).Node(owner, inner, element.hash) if err != nil { t.Fatalf("failed to retrieve node data for %x: %v", element.hash, err) } @@ -520,16 +501,12 @@ 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 := reader.Node(owner, inner, element.hash) + data, err := srcDb.Reader(srcTrie.Hash()).Node(owner, inner, element.hash) if err != nil { t.Fatalf("failed to retrieve node data for %x: %v", element.hash, err) } @@ -608,15 +585,12 @@ 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 := reader.Node(owner, inner, element.hash) + data, err := srcDb.Reader(srcTrie.Hash()).Node(owner, inner, element.hash) if err != nil { t.Fatalf("failed to retrieve node data for %x: %v", element.hash, err) } @@ -675,15 +649,11 @@ 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 := reader.Node(owner, inner, element.hash) + data, err := srcDb.Reader(root).Node(owner, inner, element.hash) if err != nil { t.Fatalf("failed to retrieve node data for hash %x: %v", element.hash, err) } diff --git a/trie/tracer_test.go b/trie/tracer_test.go index 4cec53688e..32949a5a99 100644 --- a/trie/tracer_test.go +++ b/trie/tracer_test.go @@ -226,14 +226,14 @@ func TestAccessListLeak(t *testing.T) { }{ { func(tr *Trie) { - it := tr.MustNodeIterator(nil) + it := tr.NodeIterator(nil) for it.Next(true) { } }, }, { func(tr *Trie) { - it := NewIterator(tr.MustNodeIterator(nil)) + it := NewIterator(tr.NodeIterator(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.MustNodeIterator(nil) + it = tr.NodeIterator(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.MustNodeIterator(nil) + it = tr.NodeIterator(nil) nodes = make(map[string][]byte) ) for it.Next(true) { diff --git a/trie/trie.go b/trie/trie.go index cf5d181ff7..e63b2d2830 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -39,10 +39,6 @@ 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. @@ -64,12 +60,11 @@ func (t *Trie) newFlag() nodeFlag { // Copy returns a copy of Trie. func (t *Trie) Copy() *Trie { return &Trie{ - root: t.root, - owner: t.owner, - committed: t.committed, - unhashed: t.unhashed, - reader: t.reader, - tracer: t.tracer.copy(), + root: t.root, + owner: t.owner, + unhashed: t.unhashed, + reader: t.reader, + tracer: t.tracer.copy(), } } @@ -79,7 +74,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 *Database) (*Trie, error) { +func New(id *ID, db NodeReader) (*Trie, error) { reader, err := newTrieReader(id.StateRoot, id.Owner, db) if err != nil { return nil, err @@ -105,24 +100,10 @@ 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, error) { - // Short circuit if the trie is already committed and not usable. - if t.committed { - return nil, ErrCommitted - } - return newNodeIterator(t, start), nil +func (t *Trie) NodeIterator(start []byte) NodeIterator { + return newNodeIterator(t, start) } // MustGet is a wrapper of Get and will omit any encountered error but just @@ -141,10 +122,6 @@ 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 @@ -204,10 +181,6 @@ 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 @@ -300,10 +273,6 @@ 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) } @@ -418,10 +387,6 @@ 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) @@ -609,9 +574,7 @@ 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) @@ -658,5 +621,4 @@ func (t *Trie) Reset() { t.owner = common.Hash{} t.unhashed = 0 t.tracer.reset() - t.committed = false } diff --git a/trie/trie_reader.go b/trie/trie_reader.go index d42adad2c2..51855320a6 100644 --- a/trie/trie_reader.go +++ b/trie/trie_reader.go @@ -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,6 +30,13 @@ 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 { @@ -39,16 +46,10 @@ type trieReader struct { } // newTrieReader initializes the trie reader with the given node reader. -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} +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) } return &trieReader{owner: owner, reader: reader}, nil } diff --git a/trie/trie_test.go b/trie/trie_test.go index e9278e4670..bd21a572d2 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -521,7 +521,7 @@ func runRandTest(rt randTest) bool { origin = root case opItercheckhash: checktr := NewEmpty(triedb) - it := NewIterator(tr.MustNodeIterator(nil)) + it := NewIterator(tr.NodeIterator(nil)) for it.Next() { checktr.MustUpdate(it.Key, it.Value) } @@ -530,8 +530,8 @@ func runRandTest(rt randTest) bool { } case opNodeDiff: var ( - origIter = origTrie.MustNodeIterator(nil) - curIter = tr.MustNodeIterator(nil) + origIter = origTrie.NodeIterator(nil) + curIter = tr.NodeIterator(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.MustNodeIterator(nil)) + it := NewIterator(trie.NodeIterator(nil)) for it.Next() { checktr.MustUpdate(it.Key, it.Value) } diff --git a/trie/triedb/hashdb/database.go b/trie/triedb/hashdb/database.go index ed20ff9cde..eaccfe944a 100644 --- a/trie/triedb/hashdb/database.go +++ b/trie/triedb/hashdb/database.go @@ -18,7 +18,6 @@ package hashdb import ( "errors" - "fmt" "reflect" "sync" "time" @@ -622,12 +621,8 @@ func (db *Database) Scheme() string { } // Reader retrieves a node reader belonging to the given state root. -// 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 +func (db *Database) Reader(root common.Hash) *reader { + return &reader{db: db} } // reader is a state reader of Database which implements the Reader interface.