Skip to content

feat: add read-state upgrade handler - #311

Merged
0xNilesh merged 3 commits into
testnet/donutfrom
feat/read-state-upgrade-handler
Aug 19, 2026
Merged

feat: add read-state upgrade handler#311
0xNilesh merged 3 commits into
testnet/donutfrom
feat/read-state-upgrade-handler

Conversation

@0xNilesh

Copy link
Copy Markdown
Member

Upgrade handler for the x/ucallback read-state module that landed in #310.

What it does

  • Adds the ucallback store. New module, so StoreUpgrades.Added is required — RunMigrations registers the consensus version but cannot create a store the multistore was never told about, and the node fails to load at the upgrade height without it.
  • Reserves every unclaimed system-contract address. SYSTEM_CONTRACTS is 47 entries, not 7 — constants.go's init() fills the 0xA0-0xAF / 0xB0-0xBF / 0xC0-0xCF ranges with a full proxy+admin+impl triple each. The genesis loop only runs at InitGenesis, so slots added after a chain launched were never reserved on it. Checked against live donut: 41 of the 47 are bare, including UNIVERSAL_CALLBACK (0x…C2), which x/ucallback needs before it can ingest anything. The other 6 are skipped by the already-deployed guard.

Two fixes it depends on

Both are chain halts found by running the upgrade locally, not by tests.

5f1607ab — EVM coin-info fallback. x/vm sets the coin-info global in its own PreBlock, but x/upgrade's PreBlocker runs first. Any handler touching EVM state resolves the coin denom through a still-nil global and the node dies mid-upgrade. Upstream provides a fallback for exactly this window; nothing here was populating it. Split by build tag — under -tags=test that setter writes the coin info rather than a fallback and collides with vm.InitGenesis.

4af77abc — code-hash check. isContractDeployed went through GetAccount, which loads the balance and hits the same global. Now reads the code-hash store directly. Same semantics, same F-2026-17025 sentinel guard.

FixReservedBytecode survived on donut only because it writes code without ever reading an account.

Verification

Local cosmovisor simulation, release/v1.1.39-donut → this branch, passed first attempt:

baseline          6 deployed / 41 bare  — mirrors donut
halt @97          old binary stops, cosmovisor swaps
handler           ran clean, 0 panics, 1 attempt
resumed           97 -> 174, catching_up: false
upgrade applied   height 97
ucallback         registered v1
contracts         47 checked, 0 missing

To reproduce donut's state the OLD binary was built with auto-reservation disabled — the release branch already reserves all 47 at genesis, so a stock fresh chain cannot reproduce the gap.

Plan name must be exactly read-state.

x/vm sets the coin info in its own PreBlock, but x/upgrade's PreBlocker runs
first, so any upgrade handler touching EVM state hits a nil global and halts
the chain. Upstream provides this fallback; nothing was populating it.
GetAccount reads the balance, which resolves the EVM coin denom. A code check
has no business loading balances, and it panics when run from an upgrade.
Adds the x/ucallback store and reserves every system-contract address still
unclaimed in the A/B/C ranges — 41 of 47 on donut, including 0xC2, which
x/ucallback needs before it can ingest anything.
@0xNilesh
0xNilesh merged commit 515cdb4 into testnet/donut Aug 19, 2026
7 checks passed
@0xNilesh
0xNilesh deleted the feat/read-state-upgrade-handler branch August 19, 2026 06:53
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