diff --git a/core/state/database.go b/core/state/database.go index 9467c8f72e..1022cb90dd 100644 --- a/core/state/database.go +++ b/core/state/database.go @@ -115,7 +115,7 @@ type Trie interface { // The returned nodeset can be nil if the trie is clean(nothing to commit). // Once the trie is committed, it's not usable anymore. A new trie must // be created with new root and updated trie database for following usage - Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) + Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, *trienode.Witness, error) // 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 diff --git a/core/state/snapshot/generate.go b/core/state/snapshot/generate.go index f54debebed..720d907ca8 100644 --- a/core/state/snapshot/generate.go +++ b/core/state/snapshot/generate.go @@ -362,7 +362,7 @@ func (dl *diskLayer) generateRange(ctx *generatorContext, trieId *trie.ID, prefi for i, key := range result.keys { snapTrie.Update(key, result.vals[i]) } - root, nodes, err := snapTrie.Commit(false) + root, nodes, _, err := snapTrie.Commit(false) if err != nil { return false, nil, err } diff --git a/core/state/snapshot/generate_test.go b/core/state/snapshot/generate_test.go index 07016b675c..c6b7269c82 100644 --- a/core/state/snapshot/generate_test.go +++ b/core/state/snapshot/generate_test.go @@ -209,7 +209,7 @@ func (t *testHelper) makeStorageTrie(owner common.Hash, keys []string, vals []st if !commit { return stTrie.Hash() } - root, nodes, _ := stTrie.Commit(false) + root, nodes, _, _ := stTrie.Commit(false) if nodes != nil { t.nodes.Merge(nodes) } @@ -217,7 +217,7 @@ func (t *testHelper) makeStorageTrie(owner common.Hash, keys []string, vals []st } func (t *testHelper) Commit() common.Hash { - root, nodes, _ := t.accTrie.Commit(true) + root, nodes, _, _ := t.accTrie.Commit(true) if nodes != nil { t.nodes.Merge(nodes) } diff --git a/core/state/state_object.go b/core/state/state_object.go index d42d2c34d8..66a9e7b865 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -388,7 +388,7 @@ func (s *stateObject) commit() (*trienode.NodeSet, error) { // The trie is currently in an open state and could potentially contain // cached mutations. Call commit to acquire a set of nodes that have been // modified, the set can be nil if nothing to commit. - root, nodes, err := s.trie.Commit(false) + root, nodes, _, err := s.trie.Commit(false) if err != nil { return nil, err } diff --git a/core/state/statedb.go b/core/state/statedb.go index a59de16a70..e26ab92d7e 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -1222,7 +1222,7 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er if metrics.EnabledExpensive { start = time.Now() } - root, set, err := s.trie.Commit(true) + root, set, _, err := s.trie.Commit(true) if err != nil { return common.Hash{}, err } diff --git a/eth/protocols/snap/sync_test.go b/eth/protocols/snap/sync_test.go index 1514ad4e13..47c2520694 100644 --- a/eth/protocols/snap/sync_test.go +++ b/eth/protocols/snap/sync_test.go @@ -1482,7 +1482,7 @@ func makeAccountTrieNoStorage(n int, scheme string) (string, *trie.Trie, []*kv) // Commit the state changes into db and re-create the trie // for accessing later. - root, nodes, _ := accTrie.Commit(false) + root, nodes, _, _ := accTrie.Commit(false) db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) accTrie, _ = trie.New(trie.StateTrieID(root), db) @@ -1544,7 +1544,7 @@ func makeBoundaryAccountTrie(scheme string, n int) (string, *trie.Trie, []*kv) { // Commit the state changes into db and re-create the trie // for accessing later. - root, nodes, _ := accTrie.Commit(false) + root, nodes, _, _ := accTrie.Commit(false) db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) accTrie, _ = trie.New(trie.StateTrieID(root), db) @@ -1590,7 +1590,7 @@ func makeAccountTrieWithStorageWithUniqueStorage(scheme string, accounts, slots slices.SortFunc(entries, (*kv).cmp) // Commit account trie - root, set, _ := accTrie.Commit(true) + root, set, _, _ := accTrie.Commit(true) nodes.Merge(set) // Commit gathered dirty nodes into database @@ -1655,7 +1655,7 @@ func makeAccountTrieWithStorage(scheme string, accounts, slots int, code, bounda slices.SortFunc(entries, (*kv).cmp) // Commit account trie - root, set, _ := accTrie.Commit(true) + root, set, _, _ := accTrie.Commit(true) nodes.Merge(set) // Commit gathered dirty nodes into database @@ -1697,7 +1697,7 @@ func makeStorageTrieWithSeed(owner common.Hash, n, seed uint64, db *trie.Databas entries = append(entries, elem) } slices.SortFunc(entries, (*kv).cmp) - root, nodes, _ := trie.Commit(false) + root, nodes, _, _ := trie.Commit(false) return root, nodes, entries } @@ -1748,7 +1748,7 @@ func makeBoundaryStorageTrie(owner common.Hash, n int, db *trie.Database) (commo entries = append(entries, elem) } slices.SortFunc(entries, (*kv).cmp) - root, nodes, _ := trie.Commit(false) + root, nodes, _, _ := trie.Commit(false) return root, nodes, entries } diff --git a/light/postprocess.go b/light/postprocess.go index 13d75f8617..fd49fe5608 100644 --- a/light/postprocess.go +++ b/light/postprocess.go @@ -214,7 +214,7 @@ func (c *ChtIndexerBackend) Process(ctx context.Context, header *types.Header) e // Commit implements core.ChainIndexerBackend func (c *ChtIndexerBackend) Commit() error { - root, nodes, err := c.trie.Commit(false) + root, nodes, _, err := c.trie.Commit(false) if err != nil { return err } @@ -467,7 +467,7 @@ func (b *BloomTrieIndexerBackend) Commit() error { return terr } } - root, nodes, err := b.trie.Commit(false) + root, nodes, _, err := b.trie.Commit(false) if err != nil { return err } diff --git a/light/trie.go b/light/trie.go index 1847f1e71b..0a38f41e53 100644 --- a/light/trie.go +++ b/light/trie.go @@ -177,9 +177,9 @@ func (t *odrTrie) DeleteAccount(address common.Address) error { }) } -func (t *odrTrie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) { +func (t *odrTrie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, *trienode.Witness, error) { if t.trie == nil { - return t.id.Root, nil, nil + return t.id.Root, nil, nil, nil } return t.trie.Commit(collectLeaf) } diff --git a/tests/fuzzers/stacktrie/trie_fuzzer.go b/tests/fuzzers/stacktrie/trie_fuzzer.go index 3d65524095..099d27489e 100644 --- a/tests/fuzzers/stacktrie/trie_fuzzer.go +++ b/tests/fuzzers/stacktrie/trie_fuzzer.go @@ -171,7 +171,7 @@ func (f *fuzzer) fuzz() int { return 0 } // Flush trie -> database - rootA, nodes, err := trieA.Commit(false) + rootA, nodes, _, err := trieA.Commit(false) if err != nil { panic(err) } diff --git a/tests/fuzzers/trie/trie-fuzzer.go b/tests/fuzzers/trie/trie-fuzzer.go index 687f5efb1c..c4d5af83ef 100644 --- a/tests/fuzzers/trie/trie-fuzzer.go +++ b/tests/fuzzers/trie/trie-fuzzer.go @@ -165,7 +165,7 @@ func runRandTest(rt randTest) error { case opHash: tr.Hash() case opCommit: - hash, nodes, err := tr.Commit(false) + hash, nodes, _, err := tr.Commit(false) if err != nil { return err } diff --git a/trie/committer.go b/trie/committer.go index 92163cdb3b..3acd1b7d91 100644 --- a/trie/committer.go +++ b/trie/committer.go @@ -28,15 +28,15 @@ import ( // insertion order. type committer struct { nodes *trienode.NodeSet - tracer *tracer + witness *trienode.Witness collectLeaf bool } // newCommitter creates a new committer or picks one from the pool. -func newCommitter(nodeset *trienode.NodeSet, tracer *tracer, collectLeaf bool) *committer { +func newCommitter(nodeset *trienode.NodeSet, witness *trienode.Witness, collectLeaf bool) *committer { return &committer{ nodes: nodeset, - tracer: tracer, + witness: witness, collectLeaf: collectLeaf, } } @@ -131,8 +131,7 @@ func (c *committer) store(path []byte, n node) node { // The node is embedded in its parent, in other words, this node // will not be stored in the database independently, mark it as // deleted only if the node was existent in database before. - _, ok := c.tracer.accessList[string(path)] - if ok { + if c.witness.Has(string(path)) { c.nodes.AddNode(path, trienode.NewDeleted()) } return n diff --git a/trie/iterator_test.go b/trie/iterator_test.go index 57d1f06a16..783e2d8616 100644 --- a/trie/iterator_test.go +++ b/trie/iterator_test.go @@ -59,7 +59,7 @@ func TestIterator(t *testing.T) { all[val.k] = val.v trie.MustUpdate([]byte(val.k), []byte(val.v)) } - root, nodes, _ := trie.Commit(false) + root, nodes, _, _ := trie.Commit(false) db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) trie, _ = New(TrieID(root), db) @@ -251,7 +251,7 @@ func TestDifferenceIterator(t *testing.T) { for _, val := range testdata1 { triea.MustUpdate([]byte(val.k), []byte(val.v)) } - rootA, nodesA, _ := triea.Commit(false) + rootA, nodesA, _, _ := triea.Commit(false) dba.Update(rootA, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodesA), nil) triea, _ = New(TrieID(rootA), dba) @@ -260,7 +260,7 @@ func TestDifferenceIterator(t *testing.T) { for _, val := range testdata2 { trieb.MustUpdate([]byte(val.k), []byte(val.v)) } - rootB, nodesB, _ := trieb.Commit(false) + rootB, nodesB, _, _ := trieb.Commit(false) dbb.Update(rootB, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodesB), nil) trieb, _ = New(TrieID(rootB), dbb) @@ -293,7 +293,7 @@ func TestUnionIterator(t *testing.T) { for _, val := range testdata1 { triea.MustUpdate([]byte(val.k), []byte(val.v)) } - rootA, nodesA, _ := triea.Commit(false) + rootA, nodesA, _, _ := triea.Commit(false) dba.Update(rootA, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodesA), nil) triea, _ = New(TrieID(rootA), dba) @@ -302,7 +302,7 @@ func TestUnionIterator(t *testing.T) { for _, val := range testdata2 { trieb.MustUpdate([]byte(val.k), []byte(val.v)) } - rootB, nodesB, _ := trieb.Commit(false) + rootB, nodesB, _, _ := trieb.Commit(false) dbb.Update(rootB, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodesB), nil) trieb, _ = New(TrieID(rootB), dbb) @@ -364,7 +364,7 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool, scheme string) { for _, val := range testdata1 { tr.MustUpdate([]byte(val.k), []byte(val.v)) } - root, nodes, _ := tr.Commit(false) + root, nodes, _, _ := tr.Commit(false) tdb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) if !memonly { tdb.Commit(root, false) @@ -474,7 +474,7 @@ func testIteratorContinueAfterSeekError(t *testing.T, memonly bool, scheme strin for _, val := range testdata1 { ctr.MustUpdate([]byte(val.k), []byte(val.v)) } - root, nodes, _ := ctr.Commit(false) + root, nodes, _, _ := ctr.Commit(false) for path, n := range nodes.Nodes { if n.Hash == barNodeHash { barNodePath = []byte(path) @@ -554,7 +554,7 @@ func testIteratorNodeBlob(t *testing.T, scheme string) { all[val.k] = val.v trie.MustUpdate([]byte(val.k), []byte(val.v)) } - root, nodes, _ := trie.Commit(false) + root, nodes, _, _ := trie.Commit(false) triedb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) triedb.Commit(root, false) diff --git a/trie/secure_trie.go b/trie/secure_trie.go index 7f0685e306..ed6b872fb1 100644 --- a/trie/secure_trie.go +++ b/trie/secure_trie.go @@ -223,7 +223,7 @@ func (t *StateTrie) GetKey(shaKey []byte) []byte { // All cached preimages will be also flushed if preimages recording is enabled. // Once the trie is committed, it's not usable anymore. A new trie must // be created with new root and updated trie database for following usage -func (t *StateTrie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) { +func (t *StateTrie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, *trienode.Witness, error) { // Write all the pre-images to the actual disk database if len(t.getSecKeyCache()) > 0 { if t.preimages != nil { diff --git a/trie/secure_trie_test.go b/trie/secure_trie_test.go index 2087866d38..bff5baf254 100644 --- a/trie/secure_trie_test.go +++ b/trie/secure_trie_test.go @@ -60,7 +60,7 @@ func makeTestStateTrie() (*Database, *StateTrie, map[string][]byte) { trie.MustUpdate(key, val) } } - root, nodes, _ := trie.Commit(false) + root, nodes, _, _ := trie.Commit(false) if err := triedb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil); err != nil { panic(fmt.Errorf("failed to commit db %v", err)) } diff --git a/trie/sync_test.go b/trie/sync_test.go index 3b7986ef67..1c0894ec8e 100644 --- a/trie/sync_test.go +++ b/trie/sync_test.go @@ -56,7 +56,7 @@ func makeTestTrie(scheme string) (ethdb.Database, *Database, *StateTrie, map[str trie.MustUpdate(key, val) } } - root, nodes, _ := trie.Commit(false) + root, nodes, _, _ := trie.Commit(false) if err := triedb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil); err != nil { panic(fmt.Errorf("failed to commit db %v", err)) } @@ -759,7 +759,7 @@ func testSyncMovingTarget(t *testing.T, scheme string) { srcTrie.MustUpdate(key, val) diff[string(key)] = val } - root, nodes, _ := srcTrie.Commit(false) + root, nodes, _, _ := srcTrie.Commit(false) if err := srcDb.Update(root, preRoot, 0, trienode.NewWithNodeSet(nodes), nil); err != nil { panic(err) } @@ -784,7 +784,7 @@ func testSyncMovingTarget(t *testing.T, scheme string) { srcTrie.MustUpdate([]byte(k), val) reverted[k] = val } - root, nodes, _ = srcTrie.Commit(false) + root, nodes, _, _ = srcTrie.Commit(false) if err := srcDb.Update(root, preRoot, 0, trienode.NewWithNodeSet(nodes), nil); err != nil { panic(err) } @@ -842,7 +842,7 @@ func testPivotMove(t *testing.T, scheme string, tiny bool) { writeFn([]byte{0x02, 0x34}, nil, srcTrie, stateA) writeFn([]byte{0x13, 0x44}, nil, srcTrie, stateA) - rootA, nodesA, _ := srcTrie.Commit(false) + rootA, nodesA, _, _ := srcTrie.Commit(false) if err := srcTrieDB.Update(rootA, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodesA), nil); err != nil { panic(err) } @@ -861,7 +861,7 @@ func testPivotMove(t *testing.T, scheme string, tiny bool) { deleteFn([]byte{0x13, 0x44}, srcTrie, stateB) writeFn([]byte{0x01, 0x24}, nil, srcTrie, stateB) - rootB, nodesB, _ := srcTrie.Commit(false) + rootB, nodesB, _, _ := srcTrie.Commit(false) if err := srcTrieDB.Update(rootB, rootA, 0, trienode.NewWithNodeSet(nodesB), nil); err != nil { panic(err) } @@ -879,7 +879,7 @@ func testPivotMove(t *testing.T, scheme string, tiny bool) { writeFn([]byte{0x02, 0x34}, nil, srcTrie, stateC) writeFn([]byte{0x13, 0x44}, nil, srcTrie, stateC) - rootC, nodesC, _ := srcTrie.Commit(false) + rootC, nodesC, _, _ := srcTrie.Commit(false) if err := srcTrieDB.Update(rootC, rootB, 0, trienode.NewWithNodeSet(nodesC), nil); err != nil { panic(err) } diff --git a/trie/tracer.go b/trie/tracer.go index 5786af4d3e..6ec6624b1b 100644 --- a/trie/tracer.go +++ b/trie/tracer.go @@ -16,10 +16,6 @@ package trie -import ( - "github.com/ethereum/go-ethereum/common" -) - // tracer tracks the changes of trie nodes. During the trie operations, // some nodes can be deleted from the trie, while these deleted nodes // won't be captured by trie.Hasher or trie.Committer. Thus, these deleted @@ -33,34 +29,21 @@ import ( // This tool can track all of them no matter the node is embedded in its // parent or not, but valueNode is never tracked. // -// Besides, it's also used for recording the original value of the nodes -// when they are resolved from the disk. The pre-value of the nodes will -// be used to construct trie history in the future. -// // Note tracer is not thread-safe, callers should be responsible for handling // the concurrency issues by themselves. type tracer struct { - inserts map[string]struct{} - deletes map[string]struct{} - accessList map[string][]byte + inserts map[string]struct{} + deletes map[string]struct{} } // newTracer initializes the tracer for capturing trie changes. func newTracer() *tracer { return &tracer{ - inserts: make(map[string]struct{}), - deletes: make(map[string]struct{}), - accessList: make(map[string][]byte), + inserts: make(map[string]struct{}), + deletes: make(map[string]struct{}), } } -// onRead tracks the newly loaded trie node and caches the rlp-encoded -// blob internally. Don't change the value outside of function since -// it's not deep-copied. -func (t *tracer) onRead(path []byte, val []byte) { - t.accessList[string(path)] = val -} - // onInsert tracks the newly inserted trie node. If it's already // in the deletion set (resurrected node), then just wipe it from // the deletion set as it's "untouched". @@ -83,19 +66,11 @@ func (t *tracer) onDelete(path []byte) { t.deletes[string(path)] = struct{}{} } -// reset clears the content tracked by tracer. -func (t *tracer) reset() { - t.inserts = make(map[string]struct{}) - t.deletes = make(map[string]struct{}) - t.accessList = make(map[string][]byte) -} - // copy returns a deep copied tracer instance. func (t *tracer) copy() *tracer { var ( - inserts = make(map[string]struct{}) - deletes = make(map[string]struct{}) - accessList = make(map[string][]byte) + inserts = make(map[string]struct{}) + deletes = make(map[string]struct{}) ) for path := range t.inserts { inserts[path] = struct{}{} @@ -103,27 +78,16 @@ func (t *tracer) copy() *tracer { for path := range t.deletes { deletes[path] = struct{}{} } - for path, blob := range t.accessList { - accessList[path] = common.CopyBytes(blob) - } return &tracer{ - inserts: inserts, - deletes: deletes, - accessList: accessList, + inserts: inserts, + deletes: deletes, } } -// deletedNodes returns a list of node paths which are deleted from the trie. -func (t *tracer) deletedNodes() []string { +// deleteList returns a list of node paths which are marked as deleted. +func (t *tracer) deleteList() []string { var paths []string for path := range t.deletes { - // It's possible a few deleted nodes were embedded - // in their parent before, the deletions can be no - // effect by deleting nothing, filter them out. - _, ok := t.accessList[path] - if !ok { - continue - } paths = append(paths, path) } return paths diff --git a/trie/tracer_test.go b/trie/tracer_test.go index acb8c2f6bf..a68dbcebf2 100644 --- a/trie/tracer_test.go +++ b/trie/tracer_test.go @@ -70,7 +70,7 @@ func testTrieTracer(t *testing.T, vals []struct{ k, v string }) { } insertSet := copySet(trie.tracer.inserts) // copy before commit deleteSet := copySet(trie.tracer.deletes) // copy before commit - root, nodes, _ := trie.Commit(false) + root, nodes, _, _ := trie.Commit(false) db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) seen := setKeys(iterNodes(db, root)) @@ -136,7 +136,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { for _, val := range vals { trie.MustUpdate([]byte(val.k), []byte(val.v)) } - root, nodes, _ := trie.Commit(false) + root, nodes, _, _ := trie.Commit(false) db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) trie, _ = New(TrieID(root), db) @@ -151,7 +151,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { for _, val := range vals { trie.MustUpdate([]byte(val.k), randBytes(32)) } - root, nodes, _ = trie.Commit(false) + root, nodes, _, _ = trie.Commit(false) db.Update(root, parent, 0, trienode.NewWithNodeSet(nodes), nil) trie, _ = New(TrieID(root), db) @@ -169,7 +169,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { keys = append(keys, string(key)) trie.MustUpdate(key, randBytes(32)) } - root, nodes, _ = trie.Commit(false) + root, nodes, _, _ = trie.Commit(false) db.Update(root, parent, 0, trienode.NewWithNodeSet(nodes), nil) trie, _ = New(TrieID(root), db) @@ -184,7 +184,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { for _, key := range keys { trie.MustUpdate([]byte(key), nil) } - root, nodes, _ = trie.Commit(false) + root, nodes, _, _ = trie.Commit(false) db.Update(root, parent, 0, trienode.NewWithNodeSet(nodes), nil) trie, _ = New(TrieID(root), db) @@ -199,7 +199,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) { for _, val := range vals { trie.MustUpdate([]byte(val.k), nil) } - root, nodes, _ = trie.Commit(false) + root, nodes, _, _ = trie.Commit(false) db.Update(root, parent, 0, trienode.NewWithNodeSet(nodes), nil) trie, _ = New(TrieID(root), db) @@ -218,7 +218,7 @@ func TestAccessListLeak(t *testing.T) { for _, val := range standard { trie.MustUpdate([]byte(val.k), []byte(val.v)) } - root, nodes, _ := trie.Commit(false) + root, nodes, _, _ := trie.Commit(false) db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) var cases = []struct { @@ -248,9 +248,9 @@ func TestAccessListLeak(t *testing.T) { } for _, c := range cases { trie, _ = New(TrieID(root), db) - n1 := len(trie.tracer.accessList) + n1 := trie.witness.Len() c.op(trie) - n2 := len(trie.tracer.accessList) + n2 := trie.witness.Len() if n1 != n2 { t.Fatalf("AccessList is leaked, prev %d after %d", n1, n2) @@ -268,7 +268,7 @@ func TestTinyTree(t *testing.T) { for _, val := range tiny { trie.MustUpdate([]byte(val.k), randBytes(32)) } - root, set, _ := trie.Commit(false) + root, set, _, _ := trie.Commit(false) db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(set), nil) parent := root @@ -277,7 +277,7 @@ func TestTinyTree(t *testing.T) { for _, val := range tiny { trie.MustUpdate([]byte(val.k), []byte(val.v)) } - root, set, _ = trie.Commit(false) + root, set, _, _ = trie.Commit(false) db.Update(root, parent, 0, trienode.NewWithNodeSet(set), nil) trie, _ = New(TrieID(root), db) diff --git a/trie/trie.go b/trie/trie.go index 07467ac69c..70d09d0c5a 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -53,7 +53,8 @@ type Trie struct { // tracer is the tool to track the trie changes. // It will be reset after each commit operation. - tracer *tracer + tracer *tracer + witness *trienode.Witness } // newFlag returns the cache flag value for a newly created node. @@ -70,6 +71,7 @@ func (t *Trie) Copy() *Trie { unhashed: t.unhashed, reader: t.reader, tracer: t.tracer.copy(), + witness: t.witness.Copy(), } } @@ -85,9 +87,10 @@ func New(id *ID, db *Database) (*Trie, error) { return nil, err } trie := &Trie{ - owner: id.Owner, - reader: reader, - tracer: newTracer(), + owner: id.Owner, + reader: reader, + tracer: newTracer(), + witness: trienode.NewWitness(id.Owner), } if id.Root != (common.Hash{}) && id.Root != types.EmptyRootHash { rootnode, err := trie.resolveAndTrack(id.Root[:], nil) @@ -589,7 +592,7 @@ func (t *Trie) resolveAndTrack(n hashNode, prefix []byte) (node, error) { if err != nil { return nil, err } - t.tracer.onRead(prefix, blob) + t.witness.Add(string(prefix), blob) return mustDecodeNode(n, blob), nil } @@ -607,9 +610,11 @@ func (t *Trie) Hash() common.Hash { // The returned nodeset can be nil if the trie is clean (nothing to commit). // Once the trie is committed, it's not usable anymore. A new trie must // be created with new root and updated trie database for following usage -func (t *Trie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) { - defer t.tracer.reset() +func (t *Trie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, *trienode.Witness, error) { + // Reset the trie internal states at the end. defer func() { + t.tracer = newTracer() + t.witness = trienode.NewWitness(t.owner) t.committed = true }() // Trie is empty and can be classified into two types of situations: @@ -617,15 +622,22 @@ func (t *Trie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) // (b) The trie was non-empty and all nodes are dropped => return // the node set includes all deleted nodes if t.root == nil { - paths := t.tracer.deletedNodes() + // It's possible a few deleted nodes were embedded in their parent before, + // the deletions can be no effect by deleting nothing, filter them out. + var paths []string + for _, p := range t.tracer.deleteList() { + if t.witness.Has(p) { + paths = append(paths, p) + } + } if len(paths) == 0 { - return types.EmptyRootHash, nil, nil // case (a) + return types.EmptyRootHash, nil, nil, nil // case (a) } nodes := trienode.NewNodeSet(t.owner) for _, path := range paths { nodes.AddNode([]byte(path), trienode.NewDeleted()) } - return types.EmptyRootHash, nodes, nil // case (b) + return types.EmptyRootHash, nodes, t.witness, nil // case (b) } // Derive the hash for all dirty nodes first. We hold the assumption // in the following procedure that all nodes are hashed. @@ -637,14 +649,19 @@ func (t *Trie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) // Replace the root node with the origin hash in order to // ensure all resolved nodes are dropped after the commit. t.root = hashedNode - return rootHash, nil, nil + return rootHash, nil, t.witness, nil } nodes := trienode.NewNodeSet(t.owner) - for _, path := range t.tracer.deletedNodes() { - nodes.AddNode([]byte(path), trienode.NewDeleted()) + + // It's possible a few deleted nodes were embedded in their parent before, + // the deletions can be no effect by deleting nothing, filter them out. + for _, p := range t.tracer.deleteList() { + if t.witness.Has(p) { + nodes.AddNode([]byte(p), trienode.NewDeleted()) + } } - t.root = newCommitter(nodes, t.tracer, collectLeaf).Commit(t.root) - return rootHash, nodes, nil + t.root = newCommitter(nodes, t.witness, collectLeaf).Commit(t.root) + return rootHash, nodes, t.witness, nil } // hashRoot calculates the root hash of the given trie @@ -667,6 +684,7 @@ func (t *Trie) Reset() { t.root = nil t.owner = common.Hash{} t.unhashed = 0 - t.tracer.reset() + t.tracer = newTracer() + t.witness = trienode.NewWitness(common.Hash{}) t.committed = false } diff --git a/trie/trie_test.go b/trie/trie_test.go index 8078770e7a..ee34c0080c 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -93,7 +93,7 @@ func testMissingNode(t *testing.T, memonly bool, scheme string) { trie := NewEmpty(triedb) updateString(trie, "120000", "qwerqwerqwerqwerqwerqwerqwerqwer") updateString(trie, "123456", "asdfasdfasdfasdfasdfasdfasdfasdf") - root, nodes, _ := trie.Commit(false) + root, nodes, _, _ := trie.Commit(false) triedb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) if !memonly { @@ -182,7 +182,7 @@ func TestInsert(t *testing.T) { updateString(trie, "A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") exp = common.HexToHash("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab") - root, _, _ = trie.Commit(false) + root, _, _, _ = trie.Commit(false) if root != exp { t.Errorf("case 2: exp %x got %x", exp, root) } @@ -207,7 +207,7 @@ func TestGet(t *testing.T) { if i == 1 { return } - root, nodes, _ := trie.Commit(false) + root, nodes, _, _ := trie.Commit(false) db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) trie, _ = New(TrieID(root), db) } @@ -279,7 +279,7 @@ func TestReplication(t *testing.T) { for _, val := range vals { updateString(trie, val.k, val.v) } - root, nodes, _ := trie.Commit(false) + root, nodes, _, _ := trie.Commit(false) db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) // create a new trie on top of the database and check that lookups work. @@ -292,7 +292,7 @@ func TestReplication(t *testing.T) { t.Errorf("trie2 doesn't have %q => %q", kv.k, kv.v) } } - hash, nodes, _ := trie2.Commit(false) + hash, nodes, _, _ := trie2.Commit(false) if hash != root { t.Errorf("root failure. expected %x got %x", root, hash) } @@ -506,7 +506,7 @@ func runRandTest(rt randTest) bool { case opHash: tr.Hash() case opCommit: - root, nodes, _ := tr.Commit(true) + root, nodes, _, _ := tr.Commit(true) if nodes != nil { triedb.Update(root, origin, 0, trienode.NewWithNodeSet(nodes), nil) } @@ -743,7 +743,7 @@ func TestCommitAfterHash(t *testing.T) { if exp != root { t.Errorf("got %x, exp %x", root, exp) } - root, _, _ = trie.Commit(false) + root, _, _, _ = trie.Commit(false) if exp != root { t.Errorf("got %x, exp %x", root, exp) } @@ -852,7 +852,7 @@ func TestCommitSequence(t *testing.T) { trie.MustUpdate(crypto.Keccak256(addresses[i][:]), accounts[i]) } // Flush trie -> database - root, nodes, _ := trie.Commit(false) + root, nodes, _, _ := trie.Commit(false) db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) // Flush memdb -> disk (sponge) db.Commit(root, false) @@ -893,7 +893,7 @@ func TestCommitSequenceRandomBlobs(t *testing.T) { trie.MustUpdate(key, val) } // Flush trie -> database - root, nodes, _ := trie.Commit(false) + root, nodes, _, _ := trie.Commit(false) db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) // Flush memdb -> disk (sponge) db.Commit(root, false) @@ -932,7 +932,7 @@ func TestCommitSequenceStackTrie(t *testing.T) { stTrie.Update(key, val) } // Flush trie -> database - root, nodes, _ := trie.Commit(false) + root, nodes, _, _ := trie.Commit(false) // Flush memdb -> disk (sponge) db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) db.Commit(root, false) @@ -980,7 +980,7 @@ func TestCommitSequenceSmallRoot(t *testing.T) { trie.Update(key, []byte{0x1}) stTrie.Update(key, []byte{0x1}) // Flush trie -> database - root, nodes, _ := trie.Commit(false) + root, nodes, _, _ := trie.Commit(false) // Flush memdb -> disk (sponge) db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) db.Commit(root, false) @@ -1153,7 +1153,7 @@ func benchmarkDerefRootFixedSize(b *testing.B, addresses [][20]byte, accounts [] trie.MustUpdate(crypto.Keccak256(addresses[i][:]), accounts[i]) } h := trie.Hash() - root, nodes, _ := trie.Commit(false) + root, nodes, _, _ := trie.Commit(false) triedb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) b.StartTimer() triedb.Dereference(h) diff --git a/trie/triedb/pathdb/database_test.go b/trie/triedb/pathdb/database_test.go index 912364f7f4..e2d8b62527 100644 --- a/trie/triedb/pathdb/database_test.go +++ b/trie/triedb/pathdb/database_test.go @@ -46,7 +46,7 @@ func updateTrie(addrHash common.Hash, root common.Hash, dirties, cleans map[comm h.Update(key.Bytes(), val) } } - root, nodes, _ := h.Commit(false) + root, nodes, _, _ := h.Commit(false) return root, nodes } diff --git a/trie/triedb/pathdb/testutils.go b/trie/triedb/pathdb/testutils.go index d6fdacb421..0a85ceef1b 100644 --- a/trie/triedb/pathdb/testutils.go +++ b/trie/triedb/pathdb/testutils.go @@ -80,7 +80,7 @@ func (h *testHasher) Delete(key []byte) error { // Commit computes the new hash of the states and returns the set with all // state changes. -func (h *testHasher) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) { +func (h *testHasher) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, *trienode.Witness, error) { var ( nodes = make(map[common.Hash][]byte) set = trienode.NewNodeSet(h.owner) @@ -108,7 +108,7 @@ func (h *testHasher) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, e if root == types.EmptyRootHash && h.root != types.EmptyRootHash { set.AddNode(nil, trienode.NewDeleted()) } - return root, set, nil + return root, set, nil, nil } // hash performs the hash computation upon the provided states. diff --git a/trie/trienode/witness.go b/trie/trienode/witness.go new file mode 100644 index 0000000000..ba16bdc5f8 --- /dev/null +++ b/trie/trienode/witness.go @@ -0,0 +1,61 @@ +// Copyright 2023 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see