17
17
package trie
18
18
19
19
import (
20
+ "bytes"
21
+ "fmt"
20
22
"sync"
21
23
22
24
"github.com/ethereum/go-ethereum/crypto"
@@ -54,109 +56,118 @@ func returnHasherToPool(h *hasher) {
54
56
}
55
57
56
58
// hash collapses a node down into a hash node.
57
- func (h * hasher ) hash (n node , force bool ) node {
59
+ func (h * hasher ) hash (n node , force bool ) [] byte {
58
60
// Return the cached hash if it's available
59
61
if hash , _ := n .cache (); hash != nil {
60
62
return hash
61
63
}
62
64
// Trie not processed yet, walk the children
63
65
switch n := n .(type ) {
64
66
case * shortNode :
65
- collapsed := h .hashShortNodeChildren (n )
66
- hashed := h .shortnodeToHash (collapsed , force )
67
- if hn , ok := hashed .(hashNode ); ok {
68
- n .flags .hash = hn
69
- } else {
70
- n .flags .hash = nil
67
+ enc := h .encodeShortNode (n )
68
+ if len (enc ) < 32 && ! force {
69
+ // Nodes smaller than 32 bytes are embedded directly in their parent.
70
+ // In such cases, return the raw encoded blob instead of the node hash.
71
+ // It's essential to deep-copy the node blob, as the underlying buffer
72
+ // of enc will be reused later.
73
+ buf := make ([]byte , len (enc ))
74
+ copy (buf , enc )
75
+ return buf
71
76
}
72
- return hashed
77
+ hash := h .hashData (enc )
78
+ n .flags .hash = hash
79
+ return hash
80
+
73
81
case * fullNode :
74
- collapsed := h .hashFullNodeChildren (n )
75
- hashed := h .fullnodeToHash (collapsed , force )
76
- if hn , ok := hashed .(hashNode ); ok {
77
- n .flags .hash = hn
78
- } else {
79
- n .flags .hash = nil
82
+ enc := h .encodeFullNode (n )
83
+ if len (enc ) < 32 && ! force {
84
+ // Nodes smaller than 32 bytes are embedded directly in their parent.
85
+ // In such cases, return the raw encoded blob instead of the node hash.
86
+ // It's essential to deep-copy the node blob, as the underlying buffer
87
+ // of enc will be reused later.
88
+ buf := make ([]byte , len (enc ))
89
+ copy (buf , enc )
90
+ return buf
80
91
}
81
- return hashed
82
- default :
83
- // Value and hash nodes don't have children, so they're left as were
92
+ hash := h .hashData (enc )
93
+ n .flags .hash = hash
94
+ return hash
95
+
96
+ case hashNode :
97
+ // hash nodes don't have children, so they're left as were
84
98
return n
99
+
100
+ default :
101
+ panic (fmt .Errorf ("unexpected node type, %T" , n ))
85
102
}
86
103
}
87
104
88
- // hashShortNodeChildren returns a copy of the supplied shortNode, with its child
89
- // being replaced by either the hash or an embedded node if the child is small.
90
- func (h * hasher ) hashShortNodeChildren (n * shortNode ) * shortNode {
91
- var collapsed shortNode
92
- collapsed .Key = hexToCompact (n .Key )
93
- switch n .Val .(type ) {
94
- case * fullNode , * shortNode :
95
- collapsed .Val = h .hash (n .Val , false )
96
- default :
97
- collapsed .Val = n .Val
105
+ // encodeShortNode encodes the provided shortNode into the bytes. Notably, the
106
+ // return slice must be deep-copied explicitly, otherwise the underlying slice
107
+ // will be reused later.
108
+ func (h * hasher ) encodeShortNode (n * shortNode ) []byte {
109
+ // Encode leaf node
110
+ if hasTerm (n .Key ) {
111
+ var ln leafNodeEncoder
112
+ ln .Key = hexToCompact (n .Key )
113
+ ln .Val = n .Val .(valueNode )
114
+ ln .encode (h .encbuf )
115
+ return h .encodedBytes ()
98
116
}
99
- return & collapsed
117
+ // Encode extension node
118
+ var en extNodeEncoder
119
+ en .Key = hexToCompact (n .Key )
120
+ en .Val = h .hash (n .Val , false )
121
+ en .encode (h .encbuf )
122
+ return h .encodedBytes ()
123
+ }
124
+
125
+ // fnEncoderPool is the pool for storing shared fullNode encoder to mitigate
126
+ // the significant memory allocation overhead.
127
+ var fnEncoderPool = sync.Pool {
128
+ New : func () interface {} {
129
+ var enc fullnodeEncoder
130
+ return & enc
131
+ },
100
132
}
101
133
102
- // hashFullNodeChildren returns a copy of the supplied fullNode, with its child
103
- // being replaced by either the hash or an embedded node if the child is small.
104
- func (h * hasher ) hashFullNodeChildren (n * fullNode ) * fullNode {
105
- var children [17 ]node
134
+ // encodeFullNode encodes the provided fullNode into the bytes. Notably, the
135
+ // return slice must be deep-copied explicitly, otherwise the underlying slice
136
+ // will be reused later.
137
+ func (h * hasher ) encodeFullNode (n * fullNode ) []byte {
138
+ fn := fnEncoderPool .Get ().(* fullnodeEncoder )
139
+ fn .reset ()
140
+
106
141
if h .parallel {
107
142
var wg sync.WaitGroup
108
143
for i := 0 ; i < 16 ; i ++ {
109
- if child := n .Children [i ]; child != nil {
110
- wg .Add (1 )
111
- go func (i int ) {
112
- hasher := newHasher (false )
113
- children [i ] = hasher .hash (child , false )
114
- returnHasherToPool (hasher )
115
- wg .Done ()
116
- }(i )
117
- } else {
118
- children [i ] = nilValueNode
144
+ if n .Children [i ] == nil {
145
+ continue
119
146
}
147
+ wg .Add (1 )
148
+ go func (i int ) {
149
+ defer wg .Done ()
150
+
151
+ h := newHasher (false )
152
+ fn .Children [i ] = h .hash (n .Children [i ], false )
153
+ returnHasherToPool (h )
154
+ }(i )
120
155
}
121
156
wg .Wait ()
122
157
} else {
123
158
for i := 0 ; i < 16 ; i ++ {
124
159
if child := n .Children [i ]; child != nil {
125
- children [i ] = h .hash (child , false )
126
- } else {
127
- children [i ] = nilValueNode
160
+ fn .Children [i ] = h .hash (child , false )
128
161
}
129
162
}
130
163
}
131
164
if n .Children [16 ] != nil {
132
- children [16 ] = n .Children [16 ]
133
- }
134
- return & fullNode {flags : nodeFlag {}, Children : children }
135
- }
136
-
137
- // shortNodeToHash computes the hash of the given shortNode. The shortNode must
138
- // first be collapsed, with its key converted to compact form. If the RLP-encoded
139
- // node data is smaller than 32 bytes, the node itself is returned.
140
- func (h * hasher ) shortnodeToHash (n * shortNode , force bool ) node {
141
- n .encode (h .encbuf )
142
- enc := h .encodedBytes ()
143
-
144
- if len (enc ) < 32 && ! force {
145
- return n // Nodes smaller than 32 bytes are stored inside their parent
165
+ fn .Children [16 ] = n .Children [16 ].(valueNode )
146
166
}
147
- return h .hashData (enc )
148
- }
149
-
150
- // fullnodeToHash computes the hash of the given fullNode. If the RLP-encoded
151
- // node data is smaller than 32 bytes, the node itself is returned.
152
- func (h * hasher ) fullnodeToHash (n * fullNode , force bool ) node {
153
- n .encode (h .encbuf )
154
- enc := h .encodedBytes ()
167
+ fn .encode (h .encbuf )
168
+ fnEncoderPool .Put (fn )
155
169
156
- if len (enc ) < 32 && ! force {
157
- return n // Nodes smaller than 32 bytes are stored inside their parent
158
- }
159
- return h .hashData (enc )
170
+ return h .encodedBytes ()
160
171
}
161
172
162
173
// encodedBytes returns the result of the last encoding operation on h.encbuf.
@@ -175,9 +186,10 @@ func (h *hasher) encodedBytes() []byte {
175
186
return h .tmp
176
187
}
177
188
178
- // hashData hashes the provided data
179
- func (h * hasher ) hashData (data []byte ) hashNode {
180
- n := make (hashNode , 32 )
189
+ // hashData hashes the provided data. It is safe to modify the returned slice after
190
+ // the function returns.
191
+ func (h * hasher ) hashData (data []byte ) []byte {
192
+ n := make ([]byte , 32 )
181
193
h .sha .Reset ()
182
194
h .sha .Write (data )
183
195
h .sha .Read (n )
@@ -192,20 +204,17 @@ func (h *hasher) hashDataTo(dst, data []byte) {
192
204
h .sha .Read (dst )
193
205
}
194
206
195
- // proofHash is used to construct trie proofs, and returns the 'collapsed'
196
- // node (for later RLP encoding) as well as the hashed node -- unless the
197
- // node is smaller than 32 bytes, in which case it will be returned as is.
198
- // This method does not do anything on value- or hash-nodes .
199
- func (h * hasher ) proofHash (original node ) ( collapsed , hashed node ) {
207
+ // proofHash is used to construct trie proofs, returning the rlp-encoded node blobs.
208
+ // Note, only resolved node (shortNode or fullNode) is expected for proofing.
209
+ //
210
+ // It is safe to modify the returned slice after the function returns .
211
+ func (h * hasher ) proofHash (original node ) [] byte {
200
212
switch n := original .(type ) {
201
213
case * shortNode :
202
- sn := h .hashShortNodeChildren (n )
203
- return sn , h .shortnodeToHash (sn , false )
214
+ return bytes .Clone (h .encodeShortNode (n ))
204
215
case * fullNode :
205
- fn := h .hashFullNodeChildren (n )
206
- return fn , h .fullnodeToHash (fn , false )
216
+ return bytes .Clone (h .encodeFullNode (n ))
207
217
default :
208
- // Value and hash nodes don't have children, so they're left as were
209
- return n , n
218
+ panic (fmt .Errorf ("unexpected node type, %T" , original ))
210
219
}
211
220
}
0 commit comments