Skip to content

feat(upgrade): v1.6.3 rescue migration, evm hardening (v0.6.0-tac.15) - #94

Merged
AkKoks merged 5 commits into
mainfrom
upgrade-v1.6.3
Sep 9, 2026
Merged

AkKoks merged 5 commits into
mainfrom
upgrade-v1.6.3

Conversation

@AkKoks

@AkKoks AkKoks commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Mainnet upgrade planned for Monday 2026-09-14, 10:00 UTC. Must land before 2026-09-23 15:35 UTC, when the compromised validators' self-unbonding matures.

Ships a rescue migration (app/upgrades/v1.6.3/, gated on chain-id tacchain_239-1): repays the 19 in-flight-message recipients from the old OFT escrow, funds the new OFT from the Foundation multisig, rescues the compromised validators' self-unbonding stake to a TAC-controlled account with completion times unchanged, and undelegates all 995 delegations on those validators back to their own owners. Plus cosmos/evmv0.6.0-tac.15.

Ships the hardening deferred from v1.6.2: bech32 address-length guard,
atomic StateDB.Commit, precompile out-of-gas semantics (#1049) and the
feemarket EndBlock clamp (#1259).
Repays the old OFT escrow to the 19 in-flight-message recipients, funds
the new OFT, redirects the six compromised validators' self-unbonding
stake to a TAC-controlled account, and undelegates every remaining
delegation on those validators back to its own owner.

The handler never returns an error. x/upgrade turns a handler error into
a panic in PreBlocker, so an abort would halt the chain — and since this
code is public and the accounts it touches are adversary-controlled, any
exact-match guard would be a halt trigger costing one utac. Every task
and item is isolated in a CacheContext, panics are recovered, balance
checks are sufficiency tests, and expected totals are logged rather than
asserted.

Task B sweeps the compromised accounts instead of a fixed record list, so
it still works if the stake was moved beforehand, and uses Unbond plus
SetUnbondingDelegationEntry because Undelegate can only write the entry
under the delegator. Task C, handling third-party funds, uses the stock
Undelegate and skips on error.

Gated on chain-id tacchain_239-1.
rekeyUBD is the only hand-written state surgery in the migration, and its
worst failure is silent: if the {dest, validator} pair never lands in the
completion-time queue slice, DequeueAllMatureUBDQueue never yields it,
CompleteUnbonding is never called, and the stake stays locked in
not_bonded_tokens_pool forever. Nothing surfaces the problem either — the
staking EndBlocker swallows a CompleteUnbonding error with `continue`.

Each test therefore drains the mature queue and asserts the payout would
actually fire, and fire only for the destination. Covered: a single entry,
TOE 6's two-entries-two-times shape, two entries sharing one completion
time, unrelated pairs in the same slice, merging into a record the
destination already holds, and re-keying to the current owner.

Validated by mutation: dropping the queue insert, leaving the UnbondingId
index dangling, overwriting instead of merging, fixing only the first
completion time, and discarding unrelated queue pairs are each caught.

Narrows rekeyUBD to a staking keeper plus an address codec so it can be
driven without a full app.
Rehearsed against a mainnet snapshot at height 25,017,373 through the real
two-binary path, and repeated in a second process to check determinism.
@AkKoks
AkKoks merged commit 97cee07 into main Sep 9, 2026
12 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