mesg-foundation/core

View on GitHub
app/export.go

Summary

Maintainability
A
2 hrs
Test Coverage
package app

import (
    "encoding/json"
    "log"

    abci "github.com/tendermint/tendermint/abci/types"
    tmtypes "github.com/tendermint/tendermint/types"

    "github.com/cosmos/cosmos-sdk/codec"
    sdk "github.com/cosmos/cosmos-sdk/types"
    "github.com/cosmos/cosmos-sdk/x/slashing"
    "github.com/cosmos/cosmos-sdk/x/staking"
)

// ExportAppStateAndValidators exports the state of the application for a genesis
// file.
func (app *NewApp) ExportAppStateAndValidators(
    forZeroHeight bool, jailWhiteList []string,
) (appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) {
    // as if they could withdraw from the start of the next block
    ctx := app.NewContext(true, abci.Header{Height: app.LastBlockHeight()})

    if forZeroHeight {
        app.prepForZeroHeightGenesis(ctx, jailWhiteList)
    }

    genState := app.mm.ExportGenesis(ctx)
    appState, err = codec.MarshalJSONIndent(app.cdc, genState)
    if err != nil {
        return nil, nil, err
    }

    validators = staking.WriteValidators(ctx, app.stakingKeeper)
    return appState, validators, nil
}

// prepare for fresh start at zero height
// NOTE zero height genesis is a temporary feature which will be deprecated
//      in favor of export at a block height
func (app *NewApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []string) {
    applyWhiteList := false

    //Check if there is a whitelist
    if len(jailWhiteList) > 0 {
        applyWhiteList = true
    }

    whiteListMap := make(map[string]bool)

    for _, addr := range jailWhiteList {
        _, err := sdk.ValAddressFromBech32(addr)
        if err != nil {
            log.Fatal(err)
        }
        whiteListMap[addr] = true
    }

    /* Handle fee distribution state. */

    // withdraw all validator commission
    app.stakingKeeper.IterateValidators(ctx, func(_ int64, val staking.ValidatorI) (stop bool) {
        _, err := app.distrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator())
        if err != nil {
            log.Fatal(err)
        }
        return false
    })

    // withdraw all delegator rewards
    dels := app.stakingKeeper.GetAllDelegations(ctx)
    for _, delegation := range dels {
        _, err := app.distrKeeper.WithdrawDelegationRewards(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress)
        if err != nil {
            log.Fatal(err)
        }
    }

    // clear validator slash events
    app.distrKeeper.DeleteAllValidatorSlashEvents(ctx)

    // clear validator historical rewards
    app.distrKeeper.DeleteAllValidatorHistoricalRewards(ctx)

    // set context height to zero
    height := ctx.BlockHeight()
    ctx = ctx.WithBlockHeight(0)

    // reinitialize all validators
    app.stakingKeeper.IterateValidators(ctx, func(_ int64, val staking.ValidatorI) (stop bool) {
        // donate any unwithdrawn outstanding reward fraction tokens to the community pool
        scraps := app.distrKeeper.GetValidatorOutstandingRewards(ctx, val.GetOperator())
        feePool := app.distrKeeper.GetFeePool(ctx)
        feePool.CommunityPool = feePool.CommunityPool.Add(scraps...)
        app.distrKeeper.SetFeePool(ctx, feePool)

        app.distrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator())
        return false
    })

    // reinitialize all delegations
    for _, del := range dels {
        app.distrKeeper.Hooks().BeforeDelegationCreated(ctx, del.DelegatorAddress, del.ValidatorAddress)
        app.distrKeeper.Hooks().AfterDelegationModified(ctx, del.DelegatorAddress, del.ValidatorAddress)
    }

    // reset context height
    ctx = ctx.WithBlockHeight(height)

    /* Handle staking state. */

    // iterate through redelegations, reset creation height
    app.stakingKeeper.IterateRedelegations(ctx, func(_ int64, red staking.Redelegation) (stop bool) {
        for i := range red.Entries {
            red.Entries[i].CreationHeight = 0
        }
        app.stakingKeeper.SetRedelegation(ctx, red)
        return false
    })

    // iterate through unbonding delegations, reset creation height
    app.stakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd staking.UnbondingDelegation) (stop bool) {
        for i := range ubd.Entries {
            ubd.Entries[i].CreationHeight = 0
        }
        app.stakingKeeper.SetUnbondingDelegation(ctx, ubd)
        return false
    })

    // Iterate through validators by power descending, reset bond heights, and
    // update bond intra-tx counters.
    store := ctx.KVStore(app.keys[staking.StoreKey])
    iter := sdk.KVStoreReversePrefixIterator(store, staking.ValidatorsKey)
    counter := int16(0)

    for ; iter.Valid(); iter.Next() {
        addr := sdk.ValAddress(iter.Key()[1:])
        validator, found := app.stakingKeeper.GetValidator(ctx, addr)
        if !found {
            panic("expected validator, not found")
        }

        validator.UnbondingHeight = 0
        if applyWhiteList && !whiteListMap[addr.String()] {
            validator.Jailed = true
        }

        app.stakingKeeper.SetValidator(ctx, validator)
        counter++
    }

    iter.Close()

    _ = app.stakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx)

    /* Handle slashing state. */

    // reset start height on signing infos
    app.slashingKeeper.IterateValidatorSigningInfos(
        ctx,
        func(addr sdk.ConsAddress, info slashing.ValidatorSigningInfo) (stop bool) {
            info.StartHeight = 0
            app.slashingKeeper.SetValidatorSigningInfo(ctx, addr, info)
            return false
        },
    )
}