accounts/keystore: serialize scans with deletion - #2336
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #2336 +/- ##
===========================================
- Coverage 54.08% 54.07% -0.02%
===========================================
Files 907 907
Lines 162090 162101 +11
===========================================
- Hits 87674 87654 -20
- Misses 68990 69018 +28
- Partials 5426 5429 +3
... and 27 files with indirect coverage changes
🚀 New features to boost your workflow:
|
|
codegenie review |
codegenie reviewNo credible findings. CoverageReviewed 6/6 hunks. Needs Human Attention
StatsModel: anthropic claude-opus-5 high No FindingsNo credible findings were found. |
There was a problem hiding this comment.
🟢 Ready to approve
The concurrency fix is narrowly scoped, uses consistent lock ordering to avoid deadlocks, and is backed by targeted race/regression tests matching the reported failure mode.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Pull request overview
Fixes a keystore cache/event race where a concurrent filesystem scan could re-add an account after KeyStore.Delete removed the key file and cache entry, resulting in stale wallets and out-of-order wallet events. The change serializes full keystore scans with key-file deletion inside accountCache, and adds a regression test for the “file disappears after key lookup” delete path.
Changes:
- Serialize
accountCache.scanAccounts()with key-file deletion via a newscanMu. - Route
KeyStore.DeletethroughaccountCache.deleteFileto ensure deletion is scan-serialized. - Add
TestDeleteFileDisappearsAfterKeyLookupto cover deletion behavior when the key file vanishes after successful lookup.
File summaries
| File | Description |
|---|---|
| accounts/keystore/keystore.go | Switches Delete to a scan-serialized deletion path (cache.deleteFile) and keeps wallet refresh behavior. |
| accounts/keystore/account_cache.go | Adds scanMu and uses it to serialize full scans with file deletion to prevent stale cache re-insertion. |
| accounts/keystore/keystore_test.go | Adds a regression test that simulates key-file disappearance after GetKey and asserts Delete returns os.IsNotExist. |
Review details
- Files reviewed: 3/3 changed files
- Comments generated: 0
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
Summary
The nightly race run 30603502369 failed in
accounts/keystore.TestWalletNotificationsbecause a filesystem scan could race withKeyStore.Delete: the scan could read an account before deletion, then reinsert it into the cache after the file and cached entry were removed. This left a stale wallet and emitted an out-of-orderWalletArrivedevent.This behavior is inherited from upstream go-ethereum and is tracked in ethereum/go-ethereum#29830 and the open fix attempt ethereum/go-ethereum#30053. The fix serializes complete account scans with file deletion inside
accountCache, so either the scan completes before deletion or it observes the post-deletion filesystem state. The strict event-order assertion remains unchanged. A regression test covers the error path where a key file disappears after successful key lookup.Executed tests
go test -race ./accounts/keystore -run '^TestWalletNotifications$' -count=200reproduced two failures.go test -race ./accounts/keystore -shuffle=1785471185402047521 -count=10go test -race ./accounts/keystore -run '^TestDeleteFileDisappearsAfterKeyLookup$' -count=20go test ./accounts/keystorego vet ./accounts/keystoremake lint(0 issues)diffguard --base origin/develop .(100% mutation score, 10/10 killed)gosec ./accounts/keystore/...could not build a complete SSA representation with the repository's Go 1.26 toolchain. Its reported findings were pre-existing and outside the changed lines.Rollout notes
This is a backward-compatible keystore synchronization fix. It does not affect consensus, configuration, key formats, or persisted chain data. A deletion may briefly wait for an in-progress keystore scan; no coordinated rollout is required.