@@ -35,14 +35,19 @@ var ErrNotRequested = errors.New("not requested")
35
35
// Node it already processed previously.
36
36
var ErrAlreadyProcessed = errors .New ("already processed" )
37
37
38
+ // maxFetchesPerDepth is the maximum number of pending trie nodes per depth. The
39
+ // role of this value is to limit the number of trie nodes that get expanded in
40
+ // memory if the node was configured with a significant number of peers.
41
+ const maxFetchesPerDepth = 16384
42
+
38
43
// request represents a scheduled or already in-flight state retrieval request.
39
44
type request struct {
45
+ path []byte // Merkle path leading to this node for prioritization
40
46
hash common.Hash // Hash of the Node data content to retrieve
41
47
data []byte // Data content of the Node, cached until all subtrees complete
42
48
code bool // Whether this is a code entry
43
49
44
50
parents []* request // Parent state nodes referencing this entry (notify all upon completion)
45
- depth int // Depth level within the trie the Node is located to prioritise DFS
46
51
deps int // Number of dependencies before allowed to commit this Node
47
52
48
53
callback LeafCallback // Callback to invoke if a leaf Node it reached on this branch
@@ -90,6 +95,7 @@ type Sync struct {
90
95
nodeReqs map [common.Hash ]* request // Pending requests pertaining to a trie node hash
91
96
codeReqs map [common.Hash ]* request // Pending requests pertaining to a code hash
92
97
queue * prque.Prque [int64 , any ] // Priority queue with the pending requests
98
+ fetches map [int ]int // Number of active fetches per trie node depth
93
99
bloom * SyncBloom // Bloom filter for fast state existence checks
94
100
}
95
101
@@ -101,14 +107,15 @@ func NewSync(root common.Hash, database ethdb.KeyValueReader, callback LeafCallb
101
107
nodeReqs : make (map [common.Hash ]* request ),
102
108
codeReqs : make (map [common.Hash ]* request ),
103
109
queue : prque.New [int64 , any ](nil ), // Ugh, can contain both string and hash, whyyy
110
+ fetches : make (map [int ]int ),
104
111
bloom : bloom ,
105
112
}
106
- ts .AddSubTrie (root , 0 , common.Hash {}, callback )
113
+ ts .AddSubTrie (root , nil , common.Hash {}, callback )
107
114
return ts
108
115
}
109
116
110
117
// AddSubTrie registers a new trie to the sync code, rooted at the designated parent.
111
- func (s * Sync ) AddSubTrie (root common.Hash , depth int , parent common.Hash , callback LeafCallback ) {
118
+ func (s * Sync ) AddSubTrie (root common.Hash , path [] byte , parent common.Hash , callback LeafCallback ) {
112
119
// Short circuit if the trie is empty or already known
113
120
if root == types .EmptyRootHash {
114
121
return
@@ -129,8 +136,8 @@ func (s *Sync) AddSubTrie(root common.Hash, depth int, parent common.Hash, callb
129
136
}
130
137
// Assemble the new sub-trie sync request
131
138
req := & request {
139
+ path : path ,
132
140
hash : root ,
133
- depth : depth ,
134
141
callback : callback ,
135
142
}
136
143
// If this sub-trie has a designated parent, link them together
@@ -148,7 +155,7 @@ func (s *Sync) AddSubTrie(root common.Hash, depth int, parent common.Hash, callb
148
155
// AddCodeEntry schedules the direct retrieval of a contract code that should not
149
156
// be interpreted as a trie node, but rather accepted and stored into the database
150
157
// as is.
151
- func (s * Sync ) AddCodeEntry (hash common.Hash , depth int , parent common.Hash ) {
158
+ func (s * Sync ) AddCodeEntry (hash common.Hash , path [] byte , parent common.Hash ) {
152
159
// Short circuit if the entry is empty or already known
153
160
if hash == types .EmptyCodeHash {
154
161
return
@@ -171,9 +178,9 @@ func (s *Sync) AddCodeEntry(hash common.Hash, depth int, parent common.Hash) {
171
178
}
172
179
// Assemble the new sub-trie sync request
173
180
req := & request {
174
- hash : hash ,
175
- code : true ,
176
- depth : depth ,
181
+ path : path ,
182
+ hash : hash ,
183
+ code : true ,
177
184
}
178
185
// If this sub-trie has a designated parent, link them together
179
186
if parent != (common.Hash {}) {
@@ -191,7 +198,18 @@ func (s *Sync) AddCodeEntry(hash common.Hash, depth int, parent common.Hash) {
191
198
func (s * Sync ) Missing (max int ) []common.Hash {
192
199
var requests []common.Hash
193
200
for ! s .queue .Empty () && (max == 0 || len (requests ) < max ) {
194
- requests = append (requests , s .queue .PopItem ().(common.Hash ))
201
+ // Retrieve th enext item in line
202
+ item , prio := s .queue .Peek ()
203
+
204
+ // If we have too many already-pending tasks for this depth, throttle
205
+ depth := int (prio >> 56 )
206
+ if s .fetches [depth ] > maxFetchesPerDepth {
207
+ break
208
+ }
209
+ // Item is allowed to be scheduled, add it to the task list
210
+ s .queue .Pop ()
211
+ s .fetches [depth ]++
212
+ requests = append (requests , item .(common.Hash ))
195
213
}
196
214
return requests
197
215
}
@@ -286,31 +304,35 @@ func (s *Sync) schedule(req *request) {
286
304
// is a trie node and code has same hash. In this case two elements
287
305
// with same hash and same or different depth will be pushed. But it's
288
306
// ok the worst case is the second response will be treated as duplicated.
289
- s .queue .Push (req .hash , int64 (req .depth ))
307
+ prio := int64 (len (req .path )) << 56 // depth >= 128 will never happen, storage leaves will be included in their parents
308
+ for i := 0 ; i < 14 && i < len (req .path ); i ++ {
309
+ prio |= int64 (15 - req .path [i ]) << (52 - i * 4 ) // 15-nibble => lexicographic order
310
+ }
311
+ s .queue .Push (req .hash , prio )
290
312
}
291
313
292
314
// children retrieves all the missing children of a state trie entry for future
293
315
// retrieval scheduling.
294
316
func (s * Sync ) children (req * request , object node ) ([]* request , error ) {
295
317
// Gather all the children of the Node, irrelevant whether known or not
296
318
type child struct {
297
- node node
298
- depth int
319
+ path [] byte
320
+ node node
299
321
}
300
322
var children []child
301
323
302
324
switch node := (object ).(type ) {
303
325
case * shortNode :
304
326
children = []child {{
305
- node : node .Val ,
306
- depth : req .depth + len ( node .Key ),
327
+ node : node .Val ,
328
+ path : append ( append ([] byte ( nil ), req .path ... ), node .Key ... ),
307
329
}}
308
330
case * fullNode :
309
331
for i := 0 ; i < 17 ; i ++ {
310
332
if node .Children [i ] != nil {
311
333
children = append (children , child {
312
- node : node .Children [i ],
313
- depth : req .depth + 1 ,
334
+ node : node .Children [i ],
335
+ path : append ( append ([] byte ( nil ), req .path ... ), byte ( i )) ,
314
336
})
315
337
}
316
338
}
@@ -323,7 +345,7 @@ func (s *Sync) children(req *request, object node) ([]*request, error) {
323
345
// Notify any external watcher of a new key/value Node
324
346
if req .callback != nil {
325
347
if node , ok := (child .node ).(valueNode ); ok {
326
- if err := req .callback (node , req .hash ); err != nil {
348
+ if err := req .callback (req . path , node , req .hash ); err != nil {
327
349
return nil , err
328
350
}
329
351
}
@@ -347,9 +369,9 @@ func (s *Sync) children(req *request, object node) ([]*request, error) {
347
369
}
348
370
// Locally unknown Node, schedule for retrieval
349
371
requests = append (requests , & request {
372
+ path : child .path ,
350
373
hash : hash ,
351
374
parents : []* request {req },
352
- depth : child .depth ,
353
375
callback : req .callback ,
354
376
})
355
377
}
@@ -365,9 +387,11 @@ func (s *Sync) commit(req *request) (err error) {
365
387
if req .code {
366
388
s .membatch .codes [req .hash ] = req .data
367
389
delete (s .codeReqs , req .hash )
390
+ s .fetches [len (req .path )]--
368
391
} else {
369
392
s .membatch .nodes [req .hash ] = req .data
370
393
delete (s .nodeReqs , req .hash )
394
+ s .fetches [len (req .path )]--
371
395
}
372
396
// Check all parents for completion
373
397
for _ , parent := range req .parents {
0 commit comments