Skip to content

Decode block events with the metadata at that block - #121

Merged
vedhavyas merged 1 commit into
mainfrom
fix/per-block-event-metadata
Jul 31, 2026
Merged

vedhavyas merged 1 commit into
mainfrom
fix/per-block-event-metadata

Conversation

@vedhavyas

Copy link
Copy Markdown
Contributor

We were pinning one metadata at chain head and using it to decode events from any block. That's wrong across runtime upgrades: mainnet went from spec 9 to 11 and the Balances pallet inserted event variants along the way, shifting Burned from index 11 to 12, Issued 15 to 17 and Rescinded 16 to 18. Decoding an older block with head metadata reads those variants as something else, consumes the wrong number of bytes, and every event after the first shifted one in that block is garbled or dropped. Since balances.Burned fires on essentially any block with a transfer, most of history was affected, and the staking paths swallow decode errors, so a from-genesis reindex would quietly under-populate share prices, nominators and the deposit tables rather than fail.

So now each block's events are decoded with the metadata that block actually ran. I read the runtime version at the block and cache one metadata per spec version, which is bounded by the number of runtimes the chain has had. events() no longer goes through subxt's events client (it can't take explicit metadata) and instead reads System::Events directly and decodes against the cached metadata; events_from_segments gets the same treatment. The client-level V14 pin stays for storage and constants.

This costs one extra state_getRuntimeVersion per block, which I think is the right trade for not silently losing events.

The two staking tests that were failing on this now pass against the same blocks they always read, and I made them surface the decode error instead of swallowing it, which is why this showed up as a bare "not found" before. I also added a test on block 6721910 that pins the mechanism: it has balances.Burned at position 1 and DomainEpochCompleted at 18, so under head metadata the whole tail of the block disappears.

Read the runtime version at each block and decode its events with the
metadata that block ran, caching one metadata per spec version. Runtime
upgrades insert event variants and shift the indices of later ones, so
head metadata mis-reads historical events and drops everything after the
first shifted variant in a block.
@vedhavyas
vedhavyas marked this pull request as ready for review July 31, 2026 07:43
@vedhavyas
vedhavyas added this pull request to the merge queue Jul 31, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 61a1263. Configure here.

Comment thread shared/src/subspace.rs
pub async fn events(&self) -> Result<Events<SubstrateConfig>, Error> {
let events = self.client.events().at(self.hash).await?;
Ok(events)
let metadata = self.metadata.at(self.hash).await?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong metadata on upgrade blocks

Medium Severity

events() and events_from_segments resolve metadata via metadata.at(self.hash), which reads post-execution state. A block runs on the parent runtime, so on a CodeUpdated block this returns the new metadata while events were still encoded with the old one—the same variant-shift corruption this PR aims to fix, narrowed to each upgrade block.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 61a1263. Configure here.

Merged via the queue into main with commit f66d42e Jul 31, 2026
6 checks passed
@vedhavyas
vedhavyas deleted the fix/per-block-event-metadata branch July 31, 2026 07:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant