package staking import ( "fmt" abci "github.com/tendermint/tendermint/abci/types" tmtypes "github.com/tendermint/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/staking/expected" "github.com/cosmos/cosmos-sdk/x/staking/types" ) // InitGenesis sets the pool and parameters for the provided keeper. For each // validator in data, it sets that validator in the keeper along with manually // setting the indexes. In addition, it also sets any delegations found in // data. Finally, it updates the bonded validators. // Returns final validator set after applying all declaration and delegations func InitGenesis(ctx sdk.Context, keeper Keeper, accountKeeper types.AccountKeeper, data types.GenesisState) (res []abci.ValidatorUpdate) { // We need to pretend to be "n blocks before genesis", where "n" is the // validator update delay, so that e.g. slashing periods are correctly // initialized for the validator set e.g. with a one-block offset - the // first TM block is at height 1, so state updates applied from // genesis.json are in block 0. ctx = ctx.WithBlockHeight(1 - sdk.ValidatorUpdateDelay) // manually set the total supply for staking based on accounts if not provided if data.Pool.NotBondedTokens.IsZero() { accountKeeper.IterateAccounts(ctx, func(acc auth.Account) (stop bool) { data.Pool.NotBondedTokens = data.Pool.NotBondedTokens. Add(acc.GetCoins().AmountOf(data.Params.BondDenom)) return false }, ) } keeper.SetPool(ctx, data.Pool) // TODO remove pool from genesis data and always calculate? keeper.SetParams(ctx, data.Params) keeper.SetLastTotalPower(ctx, data.LastTotalPower) for _, validator := range data.Validators { keeper.SetValidator(ctx, validator) // Manually set indices for the first time keeper.SetValidatorByConsAddr(ctx, validator) keeper.SetValidatorByPowerIndex(ctx, validator) // Call the creation hook if not exported if !data.Exported { keeper.AfterValidatorCreated(ctx, validator.OperatorAddress) } // Set timeslice if necessary if validator.IsUnbonding() { keeper.InsertValidatorQueue(ctx, validator) } } for _, delegation := range data.Delegations { // Call the before-creation hook if not exported if !data.Exported { keeper.BeforeDelegationCreated(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress) } keeper.SetDelegation(ctx, delegation) // Call the after-modification hook if not exported if !data.Exported { keeper.AfterDelegationModified(ctx, delegation.DelegatorAddress, delegation.ValidatorAddress) } } for _, ubd := range data.UnbondingDelegations { keeper.SetUnbondingDelegation(ctx, ubd) for _, entry := range ubd.Entries { keeper.InsertUBDQueue(ctx, ubd, entry.CompletionTime) } } for _, red := range data.Redelegations { keeper.SetRedelegation(ctx, red) for _, entry := range red.Entries { keeper.InsertRedelegationQueue(ctx, red, entry.CompletionTime) } } // don't need to run Tendermint updates if we exported if data.Exported { for _, lv := range data.LastValidatorPowers { keeper.SetLastValidatorPower(ctx, lv.Address, lv.Power) validator, found := keeper.GetValidator(ctx, lv.Address) if !found { panic("expected validator, not found") } update := validator.ABCIValidatorUpdate() update.Power = lv.Power // keep the next-val-set offset, use the last power for the first block res = append(res, update) } } else { res = keeper.ApplyAndReturnValidatorSetUpdates(ctx) } return res } // ExportGenesis returns a GenesisState for a given context and keeper. The // GenesisState will contain the pool, params, validators, and bonds found in // the keeper. func ExportGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { pool := keeper.GetPool(ctx) params := keeper.GetParams(ctx) lastTotalPower := keeper.GetLastTotalPower(ctx) validators := keeper.GetAllValidators(ctx) delegations := keeper.GetAllDelegations(ctx) var unbondingDelegations []types.UnbondingDelegation keeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd types.UnbondingDelegation) (stop bool) { unbondingDelegations = append(unbondingDelegations, ubd) return false }) var redelegations []types.Redelegation keeper.IterateRedelegations(ctx, func(_ int64, red types.Redelegation) (stop bool) { redelegations = append(redelegations, red) return false }) var lastValidatorPowers []types.LastValidatorPower keeper.IterateLastValidatorPowers(ctx, func(addr sdk.ValAddress, power int64) (stop bool) { lastValidatorPowers = append(lastValidatorPowers, types.LastValidatorPower{addr, power}) return false }) return types.GenesisState{ Pool: pool, Params: params, LastTotalPower: lastTotalPower, LastValidatorPowers: lastValidatorPowers, Validators: validators, Delegations: delegations, UnbondingDelegations: unbondingDelegations, Redelegations: redelegations, Exported: true, } } // WriteValidators returns a slice of bonded genesis validators. func WriteValidators(ctx sdk.Context, keeper Keeper) (vals []tmtypes.GenesisValidator) { keeper.IterateLastValidators(ctx, func(_ int64, validator expected.ValidatorI) (stop bool) { vals = append(vals, tmtypes.GenesisValidator{ PubKey: validator.GetConsPubKey(), Power: validator.GetTendermintPower(), Name: validator.GetMoniker(), }) return false }) return } // ValidateGenesis validates the provided staking genesis state to ensure the // expected invariants holds. (i.e. params in correct bounds, no duplicate validators) func ValidateGenesis(data types.GenesisState) error { err := validateGenesisStateValidators(data.Validators) if err != nil { return err } err = data.Params.Validate() if err != nil { return err } return nil } func validateGenesisStateValidators(validators []types.Validator) (err error) { addrMap := make(map[string]bool, len(validators)) for i := 0; i < len(validators); i++ { val := validators[i] strKey := string(val.ConsPubKey.Bytes()) if _, ok := addrMap[strKey]; ok { return fmt.Errorf("duplicate validator in genesis state: moniker %v, address %v", val.Description.Moniker, val.ConsAddress()) } if val.Jailed && val.IsBonded() { return fmt.Errorf("validator is bonded and jailed in genesis state: moniker %v, address %v", val.Description.Moniker, val.ConsAddress()) } if val.DelegatorShares.IsZero() && !val.IsUnbonding() { return fmt.Errorf("bonded/unbonded genesis validator cannot have zero delegator shares, validator: %v", val) } addrMap[strKey] = true } return }