Skip to content

Fix - Upgrade Handler 1.0 Lacks Idempotency for Dev Credit Minting#107

Open
ashishchandr70 wants to merge 13 commits intorelease/1.0from
fix/upgrade-handler-idempotency
Open

Fix - Upgrade Handler 1.0 Lacks Idempotency for Dev Credit Minting#107
ashishchandr70 wants to merge 13 commits intorelease/1.0from
fix/upgrade-handler-idempotency

Conversation

@ashishchandr70
Copy link
Copy Markdown
Contributor

Problem

The upgrade handler in app/upgrades/1.0/upgrades.go (section 5, lines 131-162) is not idempotent. The "One-shot dev credit mint + cleanup" section can execute multiple times if:

  • The upgrade handler is re-executed after a chain halt/restart
  • A partial failure occurs and the handler retries
  • The upgrade is manually triggered multiple times

Impact

  • Unintended token inflation: Each execution mints 1,000,000 CREDIT (1,000,000 * 10^6 = 1,000,000,000,000 base units)
  • Multiple transfers to the recipient address
  • Potential economic impact if exploited

Recommended Solution

Add an idempotency check at the start of section 5:

  1. Check if the recipient already has at least the expected amount of CREDIT
  2. If yes, skip minting/sending and only perform cleanup (burn any leftover balance, remove temp account)
  3. If no, proceed with the minting/sending flow

This ensures:

  • The handler can be safely re-executed
  • No duplicate minting occurs
  • Cleanup still happens even if minting was already completed
  • The upgrade remains a "one-shot" operation

ashishchandr70 and others added 11 commits November 13, 2025 10:46
* Upgrade to 1.0 and fix gosec issues

* Added acltypes.StoreKey to registered upgrade handlers

* Added more store upgrades

* Added remaining custom stores to storeUpgrades

* Removing ccvprovider types store key

* Changed genesis module init order

* use non-consumer modules

* WIP for handling the InitGenesis related error

* manually initialize provider module (#81)

* Renamed upgrade to match go proposal passed in staging

* Renamed upgrade back to 1.0

---------

Co-authored-by: Ashish Chandra <ashish@saga.xyz>
Co-authored-by: Brian Luk <brian6.dev@gmail.com>
Co-authored-by: Brian <45702419+lukitsbrian@users.noreply.github.com>
The setup fee in LaunchChainlet() was added to deposit without capturing
the returned coin, so the escrow account was funded with only the epoch
deposit. This fix assigns the result of deposit.Add(setupfee) back to
deposit before calling NewChainletAccount.
* use platform validator set for chainlet token distributions

* Add fallback to staking validators when no platform validators configured

- If PlatformValidators param is empty, fall back to GetValidators()
- Add guard against division by zero if no validators exist
- Fixes potential panic when PlatformValidators is not set

* Fix lint errors in billing module

* Fix params test: use nil instead of empty slice for PlatformValidators default
* fix: complete genesis export/import for all modules

Multiple custom modules only serialized params in ExportGenesis, leaving
their keeper KV state out of every exported genesis. This caused data loss
during export/import upgrades, state sync bootstraps, or genesis restarts.

Changes:
- x/chainlet: Export/import chainlets, chainlet stacks, and chainlet count
- x/escrow: Export/import chainlet accounts, denomination pools, and funders
- x/billing: Export/import billing history and validator payout history
- x/peers: Export/import peer data and chain counters

For each module:
- Extended genesis protobuf to include full KV store contents
- Updated ExportGenesis to iterate store prefixes and serialize data
- Updated InitGenesis to rehydrate from serialized data
- Added validation for duplicate entries
- Updated tests to cover new fields and validation
@lukitsbrian
Copy link
Copy Markdown
Contributor

Why would we trigger the upgrade multiple times?
Why would it matter if a partial failure occurs?

@ashishchandr70
Copy link
Copy Markdown
Contributor Author

Why would we trigger the upgrade multiple times? Why would it matter if a partial failure occurs?

In normal course, we would not. But in case it gets triggered more than once, this scenario comes into play. It is a precautionary PR.

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.

3 participants