1717package trie
1818
1919import (
20+ "bytes"
21+ "fmt"
2022 "sync"
2123
2224 "github.com/ethereum/go-ethereum/crypto"
@@ -54,109 +56,118 @@ func returnHasherToPool(h *hasher) {
5456}
5557
5658// 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 {
5860 // Return the cached hash if it's available
5961 if hash , _ := n .cache (); hash != nil {
6062 return hash
6163 }
6264 // Trie not processed yet, walk the children
6365 switch n := n .(type ) {
6466 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
7176 }
72- return hashed
77+ hash := h .hashData (enc )
78+ n .flags .hash = hash
79+ return hash
80+
7381 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
8091 }
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
8498 return n
99+
100+ default :
101+ panic (fmt .Errorf ("unexpected node type, %T" , n ))
85102 }
86103}
87104
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 ()
98116 }
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+ },
100132}
101133
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+
106141 if h .parallel {
107142 var wg sync.WaitGroup
108143 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
119146 }
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 )
120155 }
121156 wg .Wait ()
122157 } else {
123158 for i := 0 ; i < 16 ; i ++ {
124159 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 )
128161 }
129162 }
130163 }
131164 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 )
146166 }
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 )
155169
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 ()
160171}
161172
162173// encodedBytes returns the result of the last encoding operation on h.encbuf.
@@ -175,9 +186,10 @@ func (h *hasher) encodedBytes() []byte {
175186 return h .tmp
176187}
177188
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 )
181193 h .sha .Reset ()
182194 h .sha .Write (data )
183195 h .sha .Read (n )
@@ -192,20 +204,17 @@ func (h *hasher) hashDataTo(dst, data []byte) {
192204 h .sha .Read (dst )
193205}
194206
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 {
200212 switch n := original .(type ) {
201213 case * shortNode :
202- sn := h .hashShortNodeChildren (n )
203- return sn , h .shortnodeToHash (sn , false )
214+ return bytes .Clone (h .encodeShortNode (n ))
204215 case * fullNode :
205- fn := h .hashFullNodeChildren (n )
206- return fn , h .fullnodeToHash (fn , false )
216+ return bytes .Clone (h .encodeFullNode (n ))
207217 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 ))
210219 }
211220}
0 commit comments