Skip to content

Commit 7c749c9

Browse files
core/trie: remove trie tracer (ethereum#26665)
This PR contains a small portion of the full pbss PR, namely Remove the tracer from trie (and comitter), and instead using an accessList. Related changes to the Nodeset. --------- Co-authored-by: Gary Rong <[email protected]>
1 parent 15e5e61 commit 7c749c9

File tree

9 files changed

+116
-725
lines changed

9 files changed

+116
-725
lines changed

core/state/metrics.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,10 @@ package state
1919
import "github.com/ethereum/go-ethereum/metrics"
2020

2121
var (
22-
accountUpdatedMeter = metrics.NewRegisteredMeter("state/update/account", nil)
23-
storageUpdatedMeter = metrics.NewRegisteredMeter("state/update/storage", nil)
24-
accountDeletedMeter = metrics.NewRegisteredMeter("state/delete/account", nil)
25-
storageDeletedMeter = metrics.NewRegisteredMeter("state/delete/storage", nil)
26-
accountTrieUpdatedMeter = metrics.NewRegisteredMeter("state/update/accountnodes", nil)
27-
storageTriesUpdatedMeter = metrics.NewRegisteredMeter("state/update/storagenodes", nil)
28-
accountTrieDeletedMeter = metrics.NewRegisteredMeter("state/delete/accountnodes", nil)
29-
storageTriesDeletedMeter = metrics.NewRegisteredMeter("state/delete/storagenodes", nil)
22+
accountUpdatedMeter = metrics.NewRegisteredMeter("state/update/account", nil)
23+
storageUpdatedMeter = metrics.NewRegisteredMeter("state/update/storage", nil)
24+
accountDeletedMeter = metrics.NewRegisteredMeter("state/delete/account", nil)
25+
storageDeletedMeter = metrics.NewRegisteredMeter("state/delete/storage", nil)
26+
accountTrieNodesMeter = metrics.NewRegisteredMeter("state/trie/account", nil)
27+
storageTriesNodesMeter = metrics.NewRegisteredMeter("state/trie/storage", nil)
3028
)

core/state/statedb.go

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -970,13 +970,11 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
970970

971971
// Commit objects to the trie, measuring the elapsed time
972972
var (
973-
accountTrieNodesUpdated int
974-
accountTrieNodesDeleted int
975-
storageTrieNodesUpdated int
976-
storageTrieNodesDeleted int
977-
nodes = trie.NewMergedNodeSet()
973+
accountTrieNodes int
974+
storageTrieNodes int
975+
nodes = trie.NewMergedNodeSet()
976+
codeWriter = s.db.DiskDB().NewBatch()
978977
)
979-
codeWriter := s.db.DiskDB().NewBatch()
980978
for addr := range s.stateObjectsDirty {
981979
if obj := s.stateObjects[addr]; !obj.deleted {
982980
// Write any contract code associated with the state object
@@ -994,17 +992,9 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
994992
if err := nodes.Merge(set); err != nil {
995993
return common.Hash{}, err
996994
}
997-
updates, deleted := set.Size()
998-
storageTrieNodesUpdated += updates
999-
storageTrieNodesDeleted += deleted
995+
storageTrieNodes += set.Size()
1000996
}
1001997
}
1002-
// If the contract is destructed, the storage is still left in the
1003-
// database as dangling data. Theoretically it's should be wiped from
1004-
// database as well, but in hash-based-scheme it's extremely hard to
1005-
// determine that if the trie nodes are also referenced by other storage,
1006-
// and in path-based-scheme some technical challenges are still unsolved.
1007-
// Although it won't affect the correctness but please fix it TODO(rjl493456442).
1008998
}
1009999
if len(s.stateObjectsDirty) > 0 {
10101000
s.stateObjectsDirty = make(map[common.Address]struct{})
@@ -1025,7 +1015,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
10251015
if err := nodes.Merge(set); err != nil {
10261016
return common.Hash{}, err
10271017
}
1028-
accountTrieNodesUpdated, accountTrieNodesDeleted = set.Size()
1018+
accountTrieNodes = set.Size()
10291019
}
10301020
if metrics.EnabledExpensive {
10311021
s.AccountCommits += time.Since(start)
@@ -1034,10 +1024,9 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
10341024
storageUpdatedMeter.Mark(int64(s.StorageUpdated))
10351025
accountDeletedMeter.Mark(int64(s.AccountDeleted))
10361026
storageDeletedMeter.Mark(int64(s.StorageDeleted))
1037-
accountTrieUpdatedMeter.Mark(int64(accountTrieNodesUpdated))
1038-
accountTrieDeletedMeter.Mark(int64(accountTrieNodesDeleted))
1039-
storageTriesUpdatedMeter.Mark(int64(storageTrieNodesUpdated))
1040-
storageTriesDeletedMeter.Mark(int64(storageTrieNodesDeleted))
1027+
accountTrieNodesMeter.Mark(int64(accountTrieNodes))
1028+
storageTriesNodesMeter.Mark(int64(storageTrieNodes))
1029+
10411030
s.AccountUpdated, s.AccountDeleted = 0, 0
10421031
s.StorageUpdated, s.StorageDeleted = 0, 0
10431032
}

trie/committer.go

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -33,29 +33,22 @@ type leaf struct {
3333
// insertion order.
3434
type committer struct {
3535
nodes *NodeSet
36-
tracer *tracer
3736
collectLeaf bool
3837
}
3938

4039
// newCommitter creates a new committer or picks one from the pool.
41-
func newCommitter(owner common.Hash, tracer *tracer, collectLeaf bool) *committer {
40+
func newCommitter(nodes *NodeSet, collectLeaf bool) *committer {
4241
return &committer{
43-
nodes: NewNodeSet(owner),
44-
tracer: tracer,
42+
nodes: nodes,
4543
collectLeaf: collectLeaf,
4644
}
4745
}
4846

4947
// Commit collapses a node down into a hash node and returns it along with
5048
// the modified nodeset.
51-
func (c *committer) Commit(n node) (hashNode, *NodeSet) {
49+
func (c *committer) Commit(n node) hashNode {
5250
h := c.commit(nil, n)
53-
// Some nodes can be deleted from trie which can't be captured
54-
// by committer itself. Iterate all deleted nodes tracked by
55-
// tracer and marked them as deleted only if they are present
56-
// in database previously.
57-
c.tracer.markDeletions(c.nodes)
58-
return h.(hashNode), c.nodes
51+
return h.(hashNode)
5952
}
6053

6154
// commit collapses a node down into a hash node and returns it.
@@ -85,12 +78,6 @@ func (c *committer) commit(path []byte, n node) node {
8578
if hn, ok := hashedNode.(hashNode); ok {
8679
return hn
8780
}
88-
// The short node now is embedded in its parent. Mark the node as
89-
// deleted if it's present in database previously. It's equivalent
90-
// as deletion from database's perspective.
91-
if prev := c.tracer.getPrev(path); len(prev) != 0 {
92-
c.nodes.markDeleted(path, prev)
93-
}
9481
return collapsed
9582
case *fullNode:
9683
hashedKids := c.commitChildren(path, cn)
@@ -101,12 +88,6 @@ func (c *committer) commit(path []byte, n node) node {
10188
if hn, ok := hashedNode.(hashNode); ok {
10289
return hn
10390
}
104-
// The full node now is embedded in its parent. Mark the node as
105-
// deleted if it's present in database previously. It's equivalent
106-
// as deletion from database's perspective.
107-
if prev := c.tracer.getPrev(path); len(prev) != 0 {
108-
c.nodes.markDeleted(path, prev)
109-
}
11091
return collapsed
11192
case hashNode:
11293
return cn
@@ -169,7 +150,7 @@ func (c *committer) store(path []byte, n node) node {
169150
}
170151
)
171152
// Collect the dirty node to nodeset for return.
172-
c.nodes.markUpdated(path, mnode, c.tracer.getPrev(path))
153+
c.nodes.markUpdated(path, mnode)
173154

174155
// Collect the corresponding leaf node if it's required. We don't check
175156
// full node since it's impossible to store value in fullNode. The key

trie/database.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -792,13 +792,12 @@ func (db *Database) Update(nodes *MergedNodeSet) error {
792792
}
793793
for _, owner := range order {
794794
subset := nodes.sets[owner]
795-
for _, path := range subset.updates.order {
796-
n, ok := subset.updates.nodes[path]
797-
if !ok {
798-
return fmt.Errorf("missing node %x %v", owner, path)
795+
subset.forEachWithOrder(false, func(path string, n *memoryNode) {
796+
if n.isDeleted() {
797+
return // ignore deletion
799798
}
800799
db.insert(n.hash, int(n.size), n.node)
801-
}
800+
})
802801
}
803802
// Link up the account trie and storage trie if the node points
804803
// to an account trie leaf.

trie/nodeset.go

Lines changed: 65 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package trie
1919
import (
2020
"fmt"
2121
"reflect"
22+
"sort"
2223
"strings"
2324

2425
"github.com/ethereum/go-ethereum/common"
@@ -40,8 +41,8 @@ var memoryNodeSize = int(reflect.TypeOf(memoryNode{}).Size())
4041

4142
// memorySize returns the total memory size used by this node.
4243
// nolint:unused
43-
func (n *memoryNode) memorySize(key int) int {
44-
return int(n.size) + memoryNodeSize + key
44+
func (n *memoryNode) memorySize(pathlen int) int {
45+
return int(n.size) + memoryNodeSize + pathlen
4546
}
4647

4748
// rlp returns the raw rlp encoded blob of the cached trie node, either directly
@@ -64,96 +65,107 @@ func (n *memoryNode) obj() node {
6465
return expandNode(n.hash[:], n.node)
6566
}
6667

68+
// isDeleted returns the indicator if the node is marked as deleted.
69+
func (n *memoryNode) isDeleted() bool {
70+
return n.hash == (common.Hash{})
71+
}
72+
6773
// nodeWithPrev wraps the memoryNode with the previous node value.
74+
// nolint: unused
6875
type nodeWithPrev struct {
6976
*memoryNode
7077
prev []byte // RLP-encoded previous value, nil means it's non-existent
7178
}
7279

7380
// unwrap returns the internal memoryNode object.
74-
// nolint:unused
81+
// nolint: unused
7582
func (n *nodeWithPrev) unwrap() *memoryNode {
7683
return n.memoryNode
7784
}
7885

7986
// memorySize returns the total memory size used by this node. It overloads
8087
// the function in memoryNode by counting the size of previous value as well.
8188
// nolint: unused
82-
func (n *nodeWithPrev) memorySize(key int) int {
83-
return n.memoryNode.memorySize(key) + len(n.prev)
84-
}
85-
86-
// nodesWithOrder represents a collection of dirty nodes which includes
87-
// newly-inserted and updated nodes. The modification order of all nodes
88-
// is represented by order list.
89-
type nodesWithOrder struct {
90-
order []string // the path list of dirty nodes, sort by insertion order
91-
nodes map[string]*nodeWithPrev // the map of dirty nodes, keyed by node path
89+
func (n *nodeWithPrev) memorySize(pathlen int) int {
90+
return n.memoryNode.memorySize(pathlen) + len(n.prev)
9291
}
9392

9493
// NodeSet contains all dirty nodes collected during the commit operation.
9594
// Each node is keyed by path. It's not thread-safe to use.
9695
type NodeSet struct {
97-
owner common.Hash // the identifier of the trie
98-
updates *nodesWithOrder // the set of updated nodes(newly inserted, updated)
99-
deletes map[string][]byte // the map of deleted nodes, keyed by node
100-
leaves []*leaf // the list of dirty leaves
96+
owner common.Hash // the identifier of the trie
97+
nodes map[string]*memoryNode // the set of dirty nodes(inserted, updated, deleted)
98+
leaves []*leaf // the list of dirty leaves
99+
accessList map[string][]byte // The list of accessed nodes, which records the original node value
101100
}
102101

103102
// NewNodeSet initializes an empty node set to be used for tracking dirty nodes
104-
// from a specific account or storage trie. The owner is zero for the account
105-
// trie and the owning account address hash for storage tries.
106-
func NewNodeSet(owner common.Hash) *NodeSet {
103+
// for a specific account or storage trie. The owner is zero for the account trie
104+
// and the owning account address hash for storage tries. The provided accessList
105+
// represents the original value of accessed nodes, it can be optional but would
106+
// be beneficial for speeding up the construction of trie history.
107+
func NewNodeSet(owner common.Hash, accessList map[string][]byte) *NodeSet {
108+
// Don't panic for lazy users
109+
if accessList == nil {
110+
accessList = make(map[string][]byte)
111+
}
107112
return &NodeSet{
108-
owner: owner,
109-
updates: &nodesWithOrder{
110-
nodes: make(map[string]*nodeWithPrev),
111-
},
112-
deletes: make(map[string][]byte),
113+
owner: owner,
114+
nodes: make(map[string]*memoryNode),
115+
accessList: accessList,
113116
}
114117
}
115118

116-
/*
117-
// NewNodeSetWithDeletion initializes the nodeset with provided deletion set.
118-
func NewNodeSetWithDeletion(owner common.Hash, paths [][]byte, prev [][]byte) *NodeSet {
119-
set := NewNodeSet(owner)
120-
for i, path := range paths {
121-
set.markDeleted(path, prev[i])
119+
// forEachWithOrder iterates the dirty nodes with the specified order.
120+
// If topToBottom is true:
121+
//
122+
// then the order of iteration is top to bottom, left to right.
123+
//
124+
// If topToBottom is false:
125+
//
126+
// then the order of iteration is bottom to top, right to left.
127+
func (set *NodeSet) forEachWithOrder(topToBottom bool, callback func(path string, n *memoryNode)) {
128+
var paths sort.StringSlice
129+
for path := range set.nodes {
130+
paths = append(paths, path)
131+
}
132+
if topToBottom {
133+
paths.Sort()
134+
} else {
135+
sort.Sort(sort.Reverse(paths))
136+
}
137+
for _, path := range paths {
138+
callback(path, set.nodes[path])
122139
}
123-
return set
124140
}
125-
*/
126141

127142
// markUpdated marks the node as dirty(newly-inserted or updated) with provided
128143
// node path, node object along with its previous value.
129-
func (set *NodeSet) markUpdated(path []byte, node *memoryNode, prev []byte) {
130-
set.updates.order = append(set.updates.order, string(path))
131-
set.updates.nodes[string(path)] = &nodeWithPrev{
132-
memoryNode: node,
133-
prev: prev,
134-
}
144+
func (set *NodeSet) markUpdated(path []byte, node *memoryNode) {
145+
set.nodes[string(path)] = node
135146
}
136147

137148
// markDeleted marks the node as deleted with provided path and previous value.
138-
func (set *NodeSet) markDeleted(path []byte, prev []byte) {
139-
set.deletes[string(path)] = prev
149+
// nolint: unused
150+
func (set *NodeSet) markDeleted(path []byte) {
151+
set.nodes[string(path)] = &memoryNode{}
140152
}
141153

142154
// addLeaf collects the provided leaf node into set.
143155
func (set *NodeSet) addLeaf(node *leaf) {
144156
set.leaves = append(set.leaves, node)
145157
}
146158

147-
// Size returns the number of updated and deleted nodes contained in the set.
148-
func (set *NodeSet) Size() (int, int) {
149-
return len(set.updates.order), len(set.deletes)
159+
// Size returns the number of dirty nodes contained in the set.
160+
func (set *NodeSet) Size() int {
161+
return len(set.nodes)
150162
}
151163

152164
// Hashes returns the hashes of all updated nodes. TODO(rjl493456442) how can
153165
// we get rid of it?
154166
func (set *NodeSet) Hashes() []common.Hash {
155167
var ret []common.Hash
156-
for _, node := range set.updates.nodes {
168+
for _, node := range set.nodes {
157169
ret = append(ret, node.hash)
158170
}
159171
return ret
@@ -163,19 +175,17 @@ func (set *NodeSet) Hashes() []common.Hash {
163175
func (set *NodeSet) Summary() string {
164176
var out = new(strings.Builder)
165177
fmt.Fprintf(out, "nodeset owner: %v\n", set.owner)
166-
if set.updates != nil {
167-
for _, key := range set.updates.order {
168-
updated := set.updates.nodes[key]
169-
if updated.prev != nil {
170-
fmt.Fprintf(out, " [*]: %x -> %v prev: %x\n", key, updated.hash, updated.prev)
171-
} else {
172-
fmt.Fprintf(out, " [+]: %x -> %v\n", key, updated.hash)
178+
if set.nodes != nil {
179+
for path, n := range set.nodes {
180+
// Deletion
181+
if n.isDeleted() {
182+
fmt.Fprintf(out, " [-]: %x\n", path)
183+
continue
173184
}
185+
// Update
186+
fmt.Fprintf(out, " [+]: %x -> %v\n", path, n.hash)
174187
}
175188
}
176-
for k, n := range set.deletes {
177-
fmt.Fprintf(out, " [-]: %x -> %x\n", k, n)
178-
}
179189
for _, n := range set.leaves {
180190
fmt.Fprintf(out, "[leaf]: %v\n", n)
181191
}

0 commit comments

Comments
 (0)