Skip to content

fix: read a block's event metadata from its parent state - #1506

Merged
mrq1911 merged 1 commit into
polkadot-stable2603from
stable2603-cachefix
Jul 31, 2026
Merged

fix: read a block's event metadata from its parent state#1506
mrq1911 merged 1 commit into
polkadot-stable2603from
stable2603-cachefix

Conversation

@mrq1911

@mrq1911 mrq1911 commented Jul 31, 2026

Copy link
Copy Markdown
Member

Follow-up to #1503, which is already merged and tagged v49.2.1. Fixes a cache-poisoning bug found in a security review of that PR.

The bug

layout() keyed the event-layout cache on the spec version from at's post-state, but fetched metadata by calling the runtime API at at — which executes whatever :code sits in at's post-state.

block N-1 ─┬─ :code = old
block N    ├─ executed by OLD runtime; cumulus on_initialize writes :code = NEW
           │   LastRuntimeUpgrade = old  (frame_executive only bumps it when the new runtime first runs)
block N+1  └─ executed by NEW runtime; LastRuntimeUpgrade = new

At block N the metadata call returns the new runtime's layout while the spec key is still old, so the new layout gets cached under the old spec version. Every later old-spec block then decodes with the wrong metadata — and since record spans are measured from that metadata, a wrong layout can mis-measure and swallow neighbouring records, which is the exact failure #1503 exists to eliminate. It persists until restart.

The fix

Keep the spec key from atLastRuntimeUpgrade in a block's post-state exactly names the runtime that executed it, which is the layout needed to decode its events — and take the metadata from the parent, whose post-state :code is that runtime:

layout for N   → spec old (from N)   + metadata from N-1 (old code)  ✓
layout for N+1 → spec new (from N+1) + metadata from N   (new code)  ✓

Note taking the spec key from the parent instead would mismatch in the other direction: at N+1 the parent's post-state already has the new :code but LastRuntimeUpgrade still reads old.

Falls back to at at genesis and when the parent header is unknown, so it is never worse than before.

Tests

  • metadata_comes_from_the_parent_state — the regression itself
  • genesis_and_unknown_blocks_fall_back_to_themselves — no reads at the zero hash

18 node synth tests pass; formatting clean. The same fix is on #1504 as 5eb4e9d7f.

Once this lands, v49.2.1 moves onto it — no GitHub release or Docker image was published against the old commit, so nothing downstream is pinned.

@github-actions

Copy link
Copy Markdown

Weight Diff Report

⚠️ 2 pallet(s) have changes exceeding ±10% threshold

35 extrinsic(s) changed across 2 pallet(s). New: 4. Removed: 2.

pallet_omnipool_liquidity_mining

Extrinsic RefTime Proof Size Reads Writes
create_global_farm -0.1% (119.1M → 118.9M) 8 → 7 (-1)
update_global_farm +0.8% (164.2M → 165.6M) 9 → 8 (-1)
terminate_global_farm +1.5% (137.6M → 139.6M) 8 → 7 (-1)
create_yield_farm -0.6% (186.5M → 185.5M) 12 → 11 (-1)
update_yield_farm -0.1% (191.7M → 191.6M) 12 → 11 (-1)
stop_yield_farm -0.1% (184.5M → 184.4M) 11 → 10 (-1)
resume_yield_farm +0.2% (188.5M → 188.9M) 12 → 11 (-1)
terminate_yield_farm -0.9% (135.8M → 134.6M) 9 → 8 (-1)
deposit_shares -0.3% (282.1M → 281.3M) 20 → 19 (-1)
redeposit_shares -0.1% (242.5M → 242.3M) 18 → 17 (-1)
claim_rewards +1.5% (244.9M → 248.5M) 14 → 13 (-1)
withdraw_shares +0.2% (336.4M → 337.0M) 19 → 18 (-1)
join_farms -2.3% (172.4M → 168.4M) 16 → 15 (-1)
add_liquidity_and_join_farms -2.2% (517.9M → 506.5M) 46 → 45 (-1) 25 → 26 (+1)
exit_farms +0.8% (107.7M → 108.6M) 13 → 12 (-1)
add_liquidity_stableswap_omnipool_and_join_farms ⚠️ -22.5% (2.7B → 2.1B) 80 → 77 (-3)
remove_liquidity_stableswap_omnipool_and_exit_farms ⚠️ -35.2% (1.7B → 1.1B) 64 → 63 (-1)
price_adjustment_get -1.8% (177.3M → 174.2M)

pallet_stableswap

Extrinsic RefTime Proof Size Reads Writes
create_pool +0.6% (54.4M → 54.7M)
create_pool_with_pegs -0.8% (100.2M → 99.4M)
add_assets_liquidity -0.5% (2.8B → 2.8B) 54 → 53 (-1) 18 → 17 (-1)
add_liquidity_shares -1.1% (2.1B → 2.1B) 38 → 37 (-1) 10 → 9 (-1)
remove_liquidity_one_asset -1.3% (2.1B → 2.1B) 35 → 34 (-1) 9 → 8 (-1)
remove_liquidity -0.4% (1.7B → 1.7B) 56 → 55 (-1) 14 → 13 (-1)
withdraw_asset_amount -4.3% (2.6B → 2.5B) 36 → 35 (-1) 8 → 7 (-1)
sell -1.0% (2.1B → 2.1B)
buy -1.1% (2.0B → 2.0B)
set_asset_tradable_state +6.5% (23.1M → 24.6M) 1 → 2 (+1)
update_pool_fee -2.0% (21.4M → 21.0M)
update_amplification -2.3% (22.4M → 21.9M)
update_asset_peg_source -2.9% (28.7M → 27.9M)
update_pool_max_peg_update -1.8% (26.1M → 25.6M)
router_execution_sell +0.2% (1.2B → 1.2B) ⚠️ +18.6% (13990 → 16590) 18 → 19 (+1)
router_execution_buy -0.5% (588.8M → 585.6M)
calculate_spot_price_with_fee +3.5% (494.3M → 511.8M) ⚠️ +18.6% (13990 → 16590) 17 → 18 (+1)
New extrinsics (4)
Pallet Extrinsic RefTime Proof Reads Writes
cumulus_pallet_parachain_system block_weight_tx_extension_max_weight 0 0 0 0
cumulus_pallet_parachain_system block_weight_tx_extension_stays_fraction_of_core 0 0 0 0
cumulus_pallet_parachain_system block_weight_tx_extension_full_core 0 0 0 0
pallet_xcm weigh_message 100.0K 0 0 0
Removed extrinsics (2)
Pallet Extrinsic RefTime Proof Reads Writes
pallet_evm_accounts set_ntt_minter 23.3M 0 0 1
pallet_evm_accounts clear_ntt_minter 23.2M 0 0 1

Threshold: ±10%. Base Weight::from_parts(ref_time, proof_size) compared; per-unit components ignored.

@github-actions

Copy link
Copy Markdown

Crate versions that have not been updated:

  • hydradx: v16.0.1

Runtime version has not been increased.

@mrq1911
mrq1911 merged commit 81ab2e0 into polkadot-stable2603 Jul 31, 2026
5 of 7 checks passed
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