Skip to content

Commit 3531ca2

Browse files
authored
eth/tracers: avoid unsyncronized mutations on trie database (#23632)
This PR fixes an issue in traceChain, where the statedb Commit operation was performed asynchronously with dereference-operations agains the underlying trie.Database instance. Due to how the reference counting works within the trie database (where parent count is recursively updated when new parents are added), doing dereferencing in the middle of Commit can cause the refcount to become wrong, leading to an inconsistent state. This was fixed by doing Commit/Deref from the same routine.
1 parent 92c5d10 commit 3531ca2

File tree

2 files changed

+19
-7
lines changed

2 files changed

+19
-7
lines changed

eth/state_accessor.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ func (eth *Ethereum) stateAtBlock(block *types.Block, reexec uint64, base *state
119119
// Finalize the state so any modifications are written to the trie
120120
root, err := statedb.Commit(eth.blockchain.Config().IsEIP158(current.Number()))
121121
if err != nil {
122-
return nil, err
122+
return nil, fmt.Errorf("stateAtBlock commit failed, number %d root %v: %w",
123+
current.NumberU64(), current.Root().Hex(), err)
123124
}
124125
statedb, err = state.New(root, database, nil)
125126
if err != nil {

eth/tracers/api.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,11 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
290290
}()
291291
}
292292
// Start a goroutine to feed all the blocks into the tracers
293-
begin := time.Now()
293+
var (
294+
begin = time.Now()
295+
derefTodo []common.Hash // list of hashes to dereference from the db
296+
derefsMu sync.Mutex // mutex for the derefs
297+
)
294298

295299
go func() {
296300
var (
@@ -324,6 +328,14 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
324328
return
325329
default:
326330
}
331+
// clean out any derefs
332+
derefsMu.Lock()
333+
for _, h := range derefTodo {
334+
statedb.Database().TrieDB().Dereference(h)
335+
}
336+
derefTodo = derefTodo[:0]
337+
derefsMu.Unlock()
338+
327339
// Print progress logs if long enough time elapsed
328340
if time.Since(logged) > 8*time.Second {
329341
logged = time.Now()
@@ -382,12 +394,11 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
382394
Hash: res.block.Hash(),
383395
Traces: res.results,
384396
}
397+
// Schedule any parent tries held in memory by this task for dereferencing
385398
done[uint64(result.Block)] = result
386-
387-
// Dereference any parent tries held in memory by this task
388-
if res.statedb.Database().TrieDB() != nil {
389-
res.statedb.Database().TrieDB().Dereference(res.rootref)
390-
}
399+
derefsMu.Lock()
400+
derefTodo = append(derefTodo, res.rootref)
401+
derefsMu.Unlock()
391402
// Stream completed traces to the user, aborting on the first error
392403
for result, ok := done[next]; ok; result, ok = done[next] {
393404
if len(result.Traces) > 0 || next == end.NumberU64() {

0 commit comments

Comments
 (0)