cosmos-sdk fork: genesis.go:151-158 паникует на `appd export → init` — зеркалировать существующий PoC skip pattern из delegation.go?

Публикую это для проверки сопровождающего, прежде чем открывать PR против gonka-ai/cosmos-sdk. Обнаружено при проведении симуляционных тестов для #982 (https://github.com/gonka-ai/gonka/issues/982), Фаза 1.
Резюме
gonka-ai/cosmos-sdk@v0.53.3-ps17 намеренно отключает связывание токенов для валидаторов PoC. Compute.go:SetComputeValidators создает связанные валидаторы с набором validator.Tokens, но в учетной записи модуля BondedPool нет соответствующих банковских монет. Это документированный дизайн (см. раздел gonka/docs/cosmos_changes.md «Без привязки токенов»).
Форк уже применяет соответствующие модификации, чтобы обеспечить согласованность этого дизайна на всех путях выполнения:
- x/stake/keeper/pool.go:78-92 — TotalBondedTokens выполняет итерацию валидаторов вручную вместо запроса банка (явный комментарий // PoC OVERRIDE).
- x/stakeing/keeper/delegation.go — делегат пропускает передачу монет, если validator.Description.Details == «Created after Proof of Compute» .
- x/stake/keeper/val_state_change.go — удалены переводы между связанными/несвязанными пулами.
Единственный оставшийся пробел — это x/stake/keeper/genesis.go:151-158 , который по-прежнему обеспечивает соблюдение восходящего инварианта:
BondedBalance := k . Хранитель банка. GetAllBalances (ctx, BondedPool. GetAddress ()) // ... // если баланс отличается от облигационных монет, паника, потому что генезис, скорее всего, искажен, если ! облигационныйбаланс. Равно (bondedCoins) { паника ( fmt . Sprintf("баланс связанного пула отличается от связанных монет: %s <-> %s" , BondedBalance , BondedCoins )) }
Этот инвариант является вышестоящим Cosmos-sdk и несовместим с дизайном PoC — BondedBalance всегда равен нулю (валидаторы PoC не финансируют объединенный пул), но BondedCoins — это sum(validator.Tokens), который не равен нулю для любого цикла после первого перехода эпохи PoC.
Воздействие
выводимый экспорт (который вызывает bApp.ExportAppStateAndValidators ) создает генезис JSON. выведенный init <chain> --genesis <this file> затем паникует при загрузке, потому что ставка InitGenesis попадает в этот инвариант.
Это означает, что gonka-ai/gonka не может выполнить экспорт ванильного приложения → обновление цепочки инициализации. В производстве удалось обойти эту проблему, используя исключительно локальные обработчики x/upgrade (см. inference-chain/app/upgrades/v0_2_* ), которые переносят живое состояние вместо повторной инициализации из экспортированного генезиса. Таким образом, ошибка скрыта, но не блокируется сегодня — до тех пор, пока кому-то не понадобится аварийное восстановление из заведомо исправного экспорта, или форка из генезиса, или любого другого ванильного потока импорта из экспорта.
Воспроизведение
(Как только #982 (https://github.com/gonka-ai/gonka/issues/982) наступит фаза 1.) Запустите любую симуляцию, которая пересекает границу эпохи, а затем:
экспортировано, _:= bApp. ExportAppStateAndValidators ( false , [] string {}, [] string {}) newApp := /* новое приложение через app.New */ newApp . Менеджер модулей. InitGenesis (ctxB, newApp. AppCodec(),genesisState) // паника: баланс бонд-пула отличается от бонд-монет: <-> 906008476161stake
Сайт паники — x/stake/keeper/genesis.go:158.
Новый TestAppImportExport_Postrun в inference-chain/app/sim_test.go воспроизводит это при первом переходе эпохи PoC.
Предлагаемое исправление
Примените тот же шаблон пропуска, который уже существует в Delegation.go:
BondedBalance := k.bankKeeper.GetAllBalances(ctx, BondedPool.GetAddress()) // ... + // Валидаторы PoC не связаны по своей природе; их токены не обеспечены + // банковскими монетами (см. Compute.go:SetComputeValidators и раздел «No Token Bonding» + // в gonka/docs/cosmos_changes.md). Вычтите сумму их токенов перед + // сравнением с 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) + } + } + ожидается := BondedCoins.Sub(sdk.NewCoin(data.Params.BondDenom, pocBonded)) - if !bondedBalance.Equal(bondedCoins) { + if !bondedBalance.Equal(expected) { паника(fmt.Sprintf("баланс связанного пула отличается от связанных монет: %s <-> %s", BondedBalance, BondedCoins)) }
(Тот же шаблон необходим для проверки notBondedBalance в строках 173–175 — хотя на практике вероятность его срабатывания меньше, поскольку валидаторы PoC всегда имеют статус Bonded.)
Открытые вопросы для сопровождающих
- Соответствует ли оформление вашему замыслу? В частности: считается ли экспорт appd → init поддерживаемым потоком для этого форка или от него намеренно отказались в пользу локальных обработчиков x/upgrade?
- Соответствие строки Description.Details == «Created after Proof of Compute» является хрупким (уже используется в Delegation.go , поэтому соответствует существующему соглашению о разветвлении). Будет ли предпочтительнее использовать типизированный флаг в Валидаторе или вам хотелось бы сохранить соглашение о совпадении строк?
- Исправление намеренно локально для форка — исходный код Cosmos-sdk не должен выполнять проверку, специфичную для PoC. Подтвердите, что это соответствует вашему архитектурному направлению?
Ссылки на существующие модификации вилки
Предлагаемое исправление шаблонов зеркал уже присутствует в этом форке:
- x/stake/keeper/delegation.go — Пропуск делегирования для валидаторов PoC (коммит johnlong, 2024-08 «Рабочая версия»; усилено 10 января 2025 г. «Добавить делегирование в наше переопределение ставок»; позже «Защита только для Genesis» от Глеба Моргачева 30 сентября 2025 г. добавлен жесткий бан после генезиса).
- x/stake/keeper/pool.go — ручная итерация TotalBondedTokens («Защита только Genesis», «Обработка пропущенных блоков», Глеб Моргачев, 2025-09).
- x/stakeing/keeper/val_state_change.go — удалены связанные/не связанные переводы.
- x/stake/keeper/compute.go — SetComputeValidators (несколько коммитов до 21 декабря 2025 г., в основном Глеб Моргачев и Дима).
Отсутствующая модификация в Genesis.go:157-158 — единственный пробел в этом наборе.
Готов к пиару
Рад открыть PR против реализации gonka-ai/cosmos-sdk исправления, указанного выше (только для Genesis.go, ~10 строк, отражающих существующее соглашение), если я правильно понимаю замысел проекта. Подожду, пока специалист по сопровождению не подтвердит, чтобы не тратить зря время на поездку туда и обратно.

Posting this for maintainer validation before opening a PR against gonka-ai/cosmos-sdk . Discovered while implementing simulation tests for #982 (https://github.com/gonka-ai/gonka/issues/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 — TotalBondedTokens iterates validators manually instead of querying bank (explicit // PoC OVERRIDE comment).
- x/staking/keeper/delegation.go — Delegate skips coin transfer when validator.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 #982 (https://github.com/gonka-ai/gonka/issues/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 → init considered a supported flow for this fork, or has it been intentionally given up in favor of x/upgrade in-place handlers?
- The Description.Details == "Created after Proof of Compute" string match is fragile (already used in delegation.go , so consistent with existing fork convention). Would a typed flag on Validator be 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 — Delegate skip for PoC validators (commit by johnlong , 2024-08 "Working version"; reinforced 2025-01-10 "Add delegation to our staking override"; later "Genesis only protection" by Gleb Morgachev 2025-09-30 added a hard post-genesis ban).
- x/staking/keeper/pool.go — TotalBondedTokens manual iteration ("Genesis only protection", "Handle missed blocks" by Gleb 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, mainly Gleb Morgachev and dima ).
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.
Публикую это для проверки сопровождающего, прежде чем открывать PR против gonka-ai/cosmos-sdk. Обнаружено при проведении симуляционных тестов для #982 (https://github.com/gonka-ai/gonka/issues/982), Фаза 1.
Резюме
gonka-ai/cosmos-sdk@v0.53.3-ps17 намеренно отключает связывание токенов для валидаторов PoC. Compute.go:SetComputeValidators создает связанные валидаторы с набором validator.Tokens, но в учетной записи модуля BondedPool нет соответствующих банковских монет. Это документированный дизайн (см. раздел gonka/docs/cosmos_changes.md «Без привязки токенов»).
Форк уже применяет соответствующие модификации, чтобы обеспечить согласованность этого дизайна на всех путях выполнения:
Единственный оставшийся пробел — это x/stake/keeper/genesis.go:151-158 , который по-прежнему обеспечивает соблюдение восходящего инварианта:
Этот инвариант является вышестоящим Cosmos-sdk и несовместим с дизайном PoC — BondedBalance всегда равен нулю (валидаторы PoC не финансируют объединенный пул), но BondedCoins — это sum(validator.Tokens), который не равен нулю для любого цикла после первого перехода эпохи PoC.
Воздействие
выводимый экспорт (который вызывает bApp.ExportAppStateAndValidators ) создает генезис JSON. выведенный init <chain> --genesis <this file> затем паникует при загрузке, потому что ставка InitGenesis попадает в этот инвариант.
Это означает, что gonka-ai/gonka не может выполнить экспорт ванильного приложения → обновление цепочки инициализации. В производстве удалось обойти эту проблему, используя исключительно локальные обработчики x/upgrade (см. inference-chain/app/upgrades/v0_2_* ), которые переносят живое состояние вместо повторной инициализации из экспортированного генезиса. Таким образом, ошибка скрыта, но не блокируется сегодня — до тех пор, пока кому-то не понадобится аварийное восстановление из заведомо исправного экспорта, или форка из генезиса, или любого другого ванильного потока импорта из экспорта.
Воспроизведение
(Как только #982 (https://github.com/gonka-ai/gonka/issues/982) наступит фаза 1.) Запустите любую симуляцию, которая пересекает границу эпохи, а затем:
Сайт паники — x/stake/keeper/genesis.go:158.
Новый TestAppImportExport_Postrun в inference-chain/app/sim_test.go воспроизводит это при первом переходе эпохи PoC.
Предлагаемое исправление
Примените тот же шаблон пропуска, который уже существует в Delegation.go:
(Тот же шаблон необходим для проверки notBondedBalance в строках 173–175 — хотя на практике вероятность его срабатывания меньше, поскольку валидаторы PoC всегда имеют статус Bonded.)
Открытые вопросы для сопровождающих
Ссылки на существующие модификации вилки
Предлагаемое исправление шаблонов зеркал уже присутствует в этом форке:
Отсутствующая модификация в Genesis.go:157-158 — единственный пробел в этом наборе.
Готов к пиару
Рад открыть PR против реализации gonka-ai/cosmos-sdk исправления, указанного выше (только для Genesis.go, ~10 строк, отражающих существующее соглашение), если я правильно понимаю замысел проекта. Подожду, пока специалист по сопровождению не подтвердит, чтобы не тратить зря время на поездку туда и обратно.