Slashing Event Log Search Index Corruption Under Concurrent Index Write Operations
Problem Statement
The slashing event log in src/components/slashing/SlashingEventLog.tsx supports full-text search across slashing event descriptions. The search index is built client-side using lunr.js and rebuilt whenever new events arrive. The rebuildIndex() function at line 85 clears the index and re-adds all events. When two slashing events arrive within the same 50ms window (common during mass slashing of colluding nodes), the first rebuildIndex() starts, is interrupted by the second call (which clears the index again), and both attempt to add events concurrently. The resulting index contains duplicate entries for some events and missing entries for others. The search functionality returns incorrect results: searching for a known slashed node's ID returns zero results, or returns duplicate results with incorrect scores.
State Invariants & Parameters
- Search library:
lunr.js (client-side full-text)
- Max events in index: 1,000
- Index rebuild time: 15ms per 100 events
- Concurrent rebuilds: 2 overlapping
- Search accuracy target: 100% recall and precision
Affected Code Paths
src/components/slashing/SlashingEventLog.tsx:80-120 — rebuildIndex() overlapping calls
src/utils/search/slashingIndex.ts:40-65 — Lunr index management
src/hooks/useSlashingStream.ts:50-75 — WebSocket events triggering rebuild
Resolution Blueprint
- Implement a queue-based index rebuild: instead of calling
rebuildIndex() directly on each event, enqueue a rebuild request with a requestId. A single setTimeout(processQueue, 100) drains the queue and rebuilds once with all pending events.
- Use a generation counter:
indexGeneration is incremented on each rebuild start. addToIndex() checks that event.generation === currentGeneration. Stale adds are discarded.
- Implement differential indexing: instead of clearing and rebuilding, add individual events to the existing index using
lunr.Index.prototype.add(). This avoids the clear-and-rebuild race entirely.
- Add a stress test that fires 10 concurrent slashing events and verifies the search index returns exactly 10 results for a wildcard query.
Labels
Complexity: Hardcore
Layer: Core-Engine
Type: Race-Condition
Slashing Event Log Search Index Corruption Under Concurrent Index Write Operations
Problem Statement
The slashing event log in
src/components/slashing/SlashingEventLog.tsxsupports full-text search across slashing event descriptions. The search index is built client-side usinglunr.jsand rebuilt whenever new events arrive. TherebuildIndex()function at line 85 clears the index and re-adds all events. When two slashing events arrive within the same 50ms window (common during mass slashing of colluding nodes), the firstrebuildIndex()starts, is interrupted by the second call (which clears the index again), and both attempt to add events concurrently. The resulting index contains duplicate entries for some events and missing entries for others. The search functionality returns incorrect results: searching for a known slashed node's ID returns zero results, or returns duplicate results with incorrect scores.State Invariants & Parameters
lunr.js(client-side full-text)Affected Code Paths
src/components/slashing/SlashingEventLog.tsx:80-120—rebuildIndex()overlapping callssrc/utils/search/slashingIndex.ts:40-65— Lunr index managementsrc/hooks/useSlashingStream.ts:50-75— WebSocket events triggering rebuildResolution Blueprint
rebuildIndex()directly on each event, enqueue a rebuild request with arequestId. A singlesetTimeout(processQueue, 100)drains the queue and rebuilds once with all pending events.indexGenerationis incremented on each rebuild start.addToIndex()checks thatevent.generation === currentGeneration. Stale adds are discarded.lunr.Index.prototype.add(). This avoids the clear-and-rebuild race entirely.Labels
Complexity: HardcoreLayer: Core-EngineType: Race-Condition