🔄 Авто-синхронизация: из Discussion #1153 каждые 6 часов.
cosmos-sdk fork: genesis.go:151-158 panics on appd export → init — mirror existing PoC skip pattern from delegation.go?¶
Автор: @vitaly-andr · Категория: Proposals · Создано: 2026-05-08 11:47 UTC · Обновлено: 2026-05-08 12:14 UTC
📝 Описание¶
Posting this for maintainer validation before opening a PR against
gonka-ai/cosmos-sdk. Discovered while implementing simulation tests for
gonka-ai/gonka#982 Phase 1.
Summary¶
gonka-ai/cosmos-sdk@v0.53.3-ps17 deliberately disables token bonding for PoC
validators. compute.go:SetComputeValidators creates bonded validators with
validator.Tokens set but no corresponding bank coins in the BondedPool module
account. This is the documented design (see gonka/docs/cosmos_changes.md
"No Token Bonding" section).
The fork already applies the matching modifications to keep this design consistent across runtime paths:
x/staking/keeper/pool.go:78-92—TotalBondedTokensiterates validators manually instead of querying bank (explicit// PoC OVERRIDEcomment).x/staking/keeper/delegation.go—Delegateskips coin transfer whenvalidator.Description.Details == "Created after Proof of Compute".x/staking/keeper/val_state_change.go— removed transfers between bonded / not-bonded pools.
The only remaining gap is x/staking/keeper/genesis.go:151-158, which still
enforces the upstream invariant:
bondedBalance := k.bankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
// ...
// if balance is different from bonded coins panic because genesis is most likely malformed
if !bondedBalance.Equal(bondedCoins) {
panic(fmt.Sprintf("bonded pool balance is different from bonded coins: %s <-> %s", bondedBalance, bondedCoins))
}
This invariant is upstream cosmos-sdk and is incompatible with the PoC
design — bondedBalance is always zero (PoC validators don't fund bonded
pool), but bondedCoins is sum(validator.Tokens) which is non-zero for any
cycle past the first PoC epoch transition.
Impact¶
inferenced export (which calls bApp.ExportAppStateAndValidators) produces
a JSON genesis. inferenced init <chain> --genesis <that file> then panics on
boot because staking InitGenesis hits this invariant.
This means gonka-ai/gonka cannot perform a vanilla appd export → init chain
upgrade. Production has worked around this by using x/upgrade in-place
handlers exclusively (see inference-chain/app/upgrades/v0_2_*), which migrate
live state instead of re-initing from exported genesis. So the bug is latent
but not blocking today — until someone needs disaster recovery from a known-good
export, or a fork-from-genesis, or any other vanilla import-from-export flow.
Reproduction¶
(Once gonka-ai/gonka#982 Phase 1 lands.) Run any simulation that crosses an epoch boundary, then:
exported, _ := bApp.ExportAppStateAndValidators(false, []string{}, []string{})
newApp := /* fresh App via app.New */
newApp.ModuleManager.InitGenesis(ctxB, newApp.AppCodec(), genesisState)
// panics: bonded pool balance is different from bonded coins: <-> 906008476161stake
The panic site is x/staking/keeper/genesis.go:158.
The new TestAppImportExport_Postrun in inference-chain/app/sim_test.go
reproduces this on the first PoC epoch transition.
Proposed fix¶
Apply the same skip pattern that already exists in delegation.go:
bondedBalance := k.bankKeeper.GetAllBalances(ctx, bondedPool.GetAddress())
// ...
+// PoC validators are unbonded by design; their tokens are not backed by
+// bank coins (see compute.go:SetComputeValidators and the "No Token Bonding"
+// section in gonka/docs/cosmos_changes.md). Subtract their token sum before
+// comparing against bondedBalance.
+pocBonded := sdk.ZeroInt()
+for _, validator := range data.Validators {
+ if validator.Status == types.Bonded && validator.Description.Details == "Created after Proof of Compute" {
+ pocBonded = pocBonded.Add(validator.Tokens)
+ }
+}
+expected := bondedCoins.Sub(sdk.NewCoin(data.Params.BondDenom, pocBonded))
-if !bondedBalance.Equal(bondedCoins) {
+if !bondedBalance.Equal(expected) {
panic(fmt.Sprintf("bonded pool balance is different from bonded coins: %s <-> %s", bondedBalance, bondedCoins))
}
(Same pattern needed for the notBondedBalance check at line 173-175 — though
it's less likely to fire in practice because PoC validators are always Bonded
status.)
Open questions for maintainers¶
- Does the framing match your intent? Specifically: is
appd export → initconsidered a supported flow for this fork, or has it been intentionally given up in favor ofx/upgradein-place handlers? - The
Description.Details == "Created after Proof of Compute"string match is fragile (already used indelegation.go, so consistent with existing fork convention). Would a typed flag onValidatorbe preferable, or is the string-match convention something you'd like preserved for now? - The fix is intentionally local to the fork — upstream cosmos-sdk should not carry a PoC-specific check. Confirm this matches your architectural direction?
Existing fork modifications referenced¶
The proposed fix mirrors patterns already present in this fork:
x/staking/keeper/delegation.go—Delegateskip for PoC validators (commit byjohnlong, 2024-08 "Working version"; reinforced 2025-01-10 "Add delegation to our staking override"; later "Genesis only protection" byGleb Morgachev2025-09-30 added a hard post-genesis ban).x/staking/keeper/pool.go—TotalBondedTokensmanual iteration ("Genesis only protection", "Handle missed blocks" byGleb Morgachev, 2025-09).x/staking/keeper/val_state_change.go— removed bonded/not-bonded transfers.x/staking/keeper/compute.go—SetComputeValidators(multiple commits through 2025-12-21, mainlyGleb Morgachevanddima).
The missing modification in genesis.go:157-158 is the only gap in this set.
Ready to PR¶
Happy to open a PR against gonka-ai/cosmos-sdk implementing the fix above
(genesis.go only, ~10 lines mirroring existing convention) if my understanding
of the design intent is correct. Holding off until a maintainer confirms so I
don't waste a roundtrip.