Files
sonr/app/export.go

284 lines
7.9 KiB
Go
Raw Permalink Normal View History

2025-10-03 14:45:52 -04:00
// Package app provides export functionality for the Sonr blockchain application.
2024-07-05 22:20:13 -04:00
package app
import (
"encoding/json"
"fmt"
"log"
2024-10-02 01:40:49 -04:00
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
2025-10-03 14:45:52 -04:00
storetypes "cosmossdk.io/store/types"
2024-07-05 22:20:13 -04:00
servertypes "github.com/cosmos/cosmos-sdk/server/types"
sdk "github.com/cosmos/cosmos-sdk/types"
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
"github.com/cosmos/cosmos-sdk/x/staking"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)
// ExportAppStateAndValidators exports the state of the application for a genesis
2025-10-03 14:45:52 -04:00
// file. It can export at the current height or prepare the state for a zero-height
// genesis, which is useful for chain upgrades or migrations.
//
// Parameters:
// - forZeroHeight: If true, prepares the state for zero-height genesis
// - jailAllowedAddrs: List of validator addresses allowed to remain unjailed
// - modulesToExport: Specific modules to export (exports all if empty)
//
// Returns the exported application state and validator set.
func (app *ChainApp) ExportAppStateAndValidators(
forZeroHeight bool,
jailAllowedAddrs, modulesToExport []string,
) (servertypes.ExportedApp, error) {
2024-07-05 22:20:13 -04:00
// as if they could withdraw from the start of the next block
ctx := app.NewContextLegacy(true, cmtproto.Header{Height: app.LastBlockHeight()})
// We export at last height + 1, because that's the height at which
// CometBFT will start InitChain.
height := app.LastBlockHeight() + 1
if forZeroHeight {
height = 0
app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs)
}
genState, err := app.ModuleManager.ExportGenesisForModules(ctx, app.appCodec, modulesToExport)
if err != nil {
return servertypes.ExportedApp{}, err
}
appState, err := json.MarshalIndent(genState, "", " ")
if err != nil {
return servertypes.ExportedApp{}, err
}
validators, err := staking.WriteValidators(ctx, app.StakingKeeper)
2025-10-03 14:45:52 -04:00
if err != nil {
return servertypes.ExportedApp{}, err
}
2024-07-05 22:20:13 -04:00
return servertypes.ExportedApp{
AppState: appState,
Validators: validators,
Height: height,
2024-10-02 01:40:49 -04:00
ConsensusParams: app.GetConsensusParams(ctx),
2025-10-03 14:45:52 -04:00
}, nil
2024-07-05 22:20:13 -04:00
}
2025-10-03 14:45:52 -04:00
// prepForZeroHeightGenesis prepares the application state for a fresh start at zero height.
// This includes withdrawing all rewards, resetting validator states, and optionally
// jailing validators not in the allowed list. This function modifies the state to ensure
// a clean genesis export.
2024-07-05 22:20:13 -04:00
//
2025-10-03 14:45:52 -04:00
// NOTE: Zero height genesis is a temporary feature which will be deprecated
// in favor of export at a block height.
func (app *ChainApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []string) {
var err error
// Just to be safe, assert the invariants on current state.
app.CrisisKeeper.AssertInvariants(ctx)
// set context height to zero
height := ctx.BlockHeight()
ctx = ctx.WithBlockHeight(0)
2024-10-02 01:40:49 -04:00
applyAllowedAddrs := len(jailAllowedAddrs) > 0
2024-07-05 22:20:13 -04:00
// check if there is a allowed address list
2025-10-03 14:45:52 -04:00
2024-07-05 22:20:13 -04:00
allowedAddrsMap := make(map[string]bool)
for _, addr := range jailAllowedAddrs {
_, err := sdk.ValAddressFromBech32(addr)
if err != nil {
log.Fatal(err)
}
allowedAddrsMap[addr] = true
}
// Handle fee distribution state.
// withdraw all validator commission
2025-10-03 14:45:52 -04:00
err = app.StakingKeeper.IterateValidators(
ctx,
func(_ int64, val stakingtypes.ValidatorI) (stop bool) {
valBz, err := app.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator())
if err != nil {
panic(err)
}
_, _ = app.DistrKeeper.WithdrawValidatorCommission(ctx, valBz)
return false
},
)
2024-07-05 22:20:13 -04:00
if err != nil {
panic(err)
}
// withdraw all delegator rewards
dels, err := app.StakingKeeper.GetAllDelegations(ctx)
if err != nil {
panic(err)
}
for _, delegation := range dels {
2025-10-03 14:45:52 -04:00
valAddr, err := sdk.ValAddressFromBech32(delegation.ValidatorAddress)
if err != nil {
panic(err)
2024-07-05 22:20:13 -04:00
}
delAddr := sdk.MustAccAddressFromBech32(delegation.DelegatorAddress)
if _, err = app.DistrKeeper.WithdrawDelegationRewards(ctx, delAddr, valAddr); err != nil {
panic(err)
}
}
// clear validator slash events
app.DistrKeeper.DeleteAllValidatorSlashEvents(ctx)
// clear validator historical rewards
app.DistrKeeper.DeleteAllValidatorHistoricalRewards(ctx)
// reinitialize all validators
2025-10-03 14:45:52 -04:00
err = app.StakingKeeper.IterateValidators(
ctx,
func(_ int64, val stakingtypes.ValidatorI) (stop bool) {
valBz, err := app.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator())
if err != nil {
panic(err)
}
// donate any unwithdrawn outstanding reward fraction tokens to the community pool
scraps, err := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, valBz)
if err != nil {
panic(err)
}
feePool, err := app.DistrKeeper.FeePool.Get(ctx)
if err != nil {
panic(err)
}
feePool.CommunityPool = feePool.CommunityPool.Add(scraps...)
if err := app.DistrKeeper.FeePool.Set(ctx, feePool); err != nil {
panic(err)
}
2024-07-05 22:20:13 -04:00
2025-10-03 14:45:52 -04:00
if err := app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, valBz); err != nil {
panic(err)
}
return false
},
)
2024-07-05 22:20:13 -04:00
if err != nil {
panic(err)
}
// reinitialize all delegations
for _, del := range dels {
2025-10-03 14:45:52 -04:00
valAddr, err := sdk.ValAddressFromBech32(del.ValidatorAddress)
if err != nil {
panic(err)
2024-07-05 22:20:13 -04:00
}
delAddr := sdk.MustAccAddressFromBech32(del.DelegatorAddress)
2025-10-03 14:45:52 -04:00
if err := app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, delAddr, valAddr); err != nil {
2024-07-05 22:20:13 -04:00
// never called as BeforeDelegationCreated always returns nil
2025-10-03 14:45:52 -04:00
panic(fmt.Errorf("error while incrementing period: %w", err))
2024-07-05 22:20:13 -04:00
}
2025-10-03 14:45:52 -04:00
if err := app.DistrKeeper.Hooks().AfterDelegationModified(ctx, delAddr, valAddr); err != nil {
2024-07-05 22:20:13 -04:00
// never called as AfterDelegationModified always returns nil
2025-10-03 14:45:52 -04:00
panic(fmt.Errorf("error while creating a new delegation period record: %w", err))
2024-07-05 22:20:13 -04:00
}
}
// reset context height
ctx = ctx.WithBlockHeight(height)
// Handle staking state.
// iterate through redelegations, reset creation height
2025-10-03 14:45:52 -04:00
err = app.StakingKeeper.IterateRedelegations(
ctx,
func(_ int64, red stakingtypes.Redelegation) (stop bool) {
for i := range red.Entries {
red.Entries[i].CreationHeight = 0
}
err = app.StakingKeeper.SetRedelegation(ctx, red)
if err != nil {
panic(err)
}
return false
},
)
2024-07-05 22:20:13 -04:00
if err != nil {
panic(err)
}
// iterate through unbonding delegations, reset creation height
2025-10-03 14:45:52 -04:00
err = app.StakingKeeper.IterateUnbondingDelegations(
ctx,
func(_ int64, ubd stakingtypes.UnbondingDelegation) (stop bool) {
for i := range ubd.Entries {
ubd.Entries[i].CreationHeight = 0
}
err = app.StakingKeeper.SetUnbondingDelegation(ctx, ubd)
if err != nil {
panic(err)
}
return false
},
)
2024-07-05 22:20:13 -04:00
if err != nil {
panic(err)
}
// Iterate through validators by power descending, reset bond heights, and
// update bond intra-tx counters.
store := ctx.KVStore(app.GetKey(stakingtypes.StoreKey))
iter := storetypes.KVStoreReversePrefixIterator(store, stakingtypes.ValidatorsKey)
for ; iter.Valid(); iter.Next() {
addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key()))
2025-10-03 14:45:52 -04:00
validator, err := app.StakingKeeper.GetValidator(ctx, addr)
if err != nil {
2024-07-05 22:20:13 -04:00
panic("expected validator, not found")
}
validator.UnbondingHeight = 0
if applyAllowedAddrs && !allowedAddrsMap[addr.String()] {
validator.Jailed = true
}
err = app.StakingKeeper.SetValidator(ctx, validator)
if err != nil {
panic(err)
}
}
2025-10-03 14:45:52 -04:00
if err := iter.Close(); err != nil {
app.Logger().Error("error while closing the key-value store reverse prefix iterator: ", err)
2024-07-05 22:20:13 -04:00
return
}
_, err = app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx)
if err != nil {
log.Fatal(err)
}
// Handle slashing state.
// reset start height on signing infos
err = app.SlashingKeeper.IterateValidatorSigningInfos(
ctx,
func(addr sdk.ConsAddress, info slashingtypes.ValidatorSigningInfo) (stop bool) {
info.StartHeight = 0
2025-10-03 14:45:52 -04:00
if err := app.SlashingKeeper.SetValidatorSigningInfo(ctx, addr, info); err != nil {
panic(err)
2024-07-05 22:20:13 -04:00
}
return false
},
)
if err != nil {
panic(err)
}
}