Merge PR #5380: ADR 17 Implementation: Historical Module
This commit is contained in:
parent
908fd2ba78
commit
fca4cbef88
|
@ -147,6 +147,9 @@ that allows for arbitrary vesting periods.
|
||||||
* `ValidateSigCountDecorator`: Validate the number of signatures in tx based on app-parameters.
|
* `ValidateSigCountDecorator`: Validate the number of signatures in tx based on app-parameters.
|
||||||
* `IncrementSequenceDecorator`: Increments the account sequence for each signer to prevent replay attacks.
|
* `IncrementSequenceDecorator`: Increments the account sequence for each signer to prevent replay attacks.
|
||||||
* (cli) [\#5223](https://github.com/cosmos/cosmos-sdk/issues/5223) Cosmos Ledger App v2.0.0 is now supported. The changes are backwards compatible and App v1.5.x is still supported.
|
* (cli) [\#5223](https://github.com/cosmos/cosmos-sdk/issues/5223) Cosmos Ledger App v2.0.0 is now supported. The changes are backwards compatible and App v1.5.x is still supported.
|
||||||
|
* (x/staking) [\#5380](https://github.com/cosmos/cosmos-sdk/pull/5380) Introduced ability to store historical info entries in staking keeper, allows applications to introspect specified number of past headers and validator sets
|
||||||
|
* Introduces new parameter `HistoricalEntries` which allows applications to determine how many recent historical info entries they want to persist in store. Default value is 0.
|
||||||
|
* Introduces cli commands and rest routes to query historical information at a given height
|
||||||
* (modules) [\#5249](https://github.com/cosmos/cosmos-sdk/pull/5249) Funds are now allowed to be directly sent to the community pool (via the distribution module account).
|
* (modules) [\#5249](https://github.com/cosmos/cosmos-sdk/pull/5249) Funds are now allowed to be directly sent to the community pool (via the distribution module account).
|
||||||
* (keys) [\#4941](https://github.com/cosmos/cosmos-sdk/issues/4941) Introduce keybase option to allow overriding the default private key implementation of a key generated through the `keys add` cli command.
|
* (keys) [\#4941](https://github.com/cosmos/cosmos-sdk/issues/4941) Introduce keybase option to allow overriding the default private key implementation of a key generated through the `keys add` cli command.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
package staking
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking/keeper"
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BeginBlocker will persist the current header and validator set as a historical entry
|
||||||
|
// and prune the oldest entry based on the HistoricalEntries parameter
|
||||||
|
func BeginBlocker(ctx sdk.Context, k keeper.Keeper) {
|
||||||
|
k.TrackHistoricalInfo(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called every block, update validator set
|
||||||
|
func EndBlocker(ctx sdk.Context, k keeper.Keeper) []abci.ValidatorUpdate {
|
||||||
|
return k.BlockValidatorUpdates(ctx)
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ const (
|
||||||
CodeInvalidDelegation = types.CodeInvalidDelegation
|
CodeInvalidDelegation = types.CodeInvalidDelegation
|
||||||
CodeInvalidInput = types.CodeInvalidInput
|
CodeInvalidInput = types.CodeInvalidInput
|
||||||
CodeValidatorJailed = types.CodeValidatorJailed
|
CodeValidatorJailed = types.CodeValidatorJailed
|
||||||
|
CodeInvalidHistoricalInfo = types.CodeInvalidHistoricalInfo
|
||||||
CodeInvalidAddress = types.CodeInvalidAddress
|
CodeInvalidAddress = types.CodeInvalidAddress
|
||||||
CodeUnauthorized = types.CodeUnauthorized
|
CodeUnauthorized = types.CodeUnauthorized
|
||||||
CodeInternal = types.CodeInternal
|
CodeInternal = types.CodeInternal
|
||||||
|
@ -47,6 +48,7 @@ const (
|
||||||
QueryDelegatorValidator = types.QueryDelegatorValidator
|
QueryDelegatorValidator = types.QueryDelegatorValidator
|
||||||
QueryPool = types.QueryPool
|
QueryPool = types.QueryPool
|
||||||
QueryParameters = types.QueryParameters
|
QueryParameters = types.QueryParameters
|
||||||
|
QueryHistoricalInfo = types.QueryHistoricalInfo
|
||||||
MaxMonikerLength = types.MaxMonikerLength
|
MaxMonikerLength = types.MaxMonikerLength
|
||||||
MaxIdentityLength = types.MaxIdentityLength
|
MaxIdentityLength = types.MaxIdentityLength
|
||||||
MaxWebsiteLength = types.MaxWebsiteLength
|
MaxWebsiteLength = types.MaxWebsiteLength
|
||||||
|
@ -86,6 +88,10 @@ var (
|
||||||
NewDelegationResp = types.NewDelegationResp
|
NewDelegationResp = types.NewDelegationResp
|
||||||
NewRedelegationResponse = types.NewRedelegationResponse
|
NewRedelegationResponse = types.NewRedelegationResponse
|
||||||
NewRedelegationEntryResponse = types.NewRedelegationEntryResponse
|
NewRedelegationEntryResponse = types.NewRedelegationEntryResponse
|
||||||
|
NewHistoricalInfo = types.NewHistoricalInfo
|
||||||
|
MustMarshalHistoricalInfo = types.MustMarshalHistoricalInfo
|
||||||
|
MustUnmarshalHistoricalInfo = types.MustUnmarshalHistoricalInfo
|
||||||
|
UnmarshalHistoricalInfo = types.UnmarshalHistoricalInfo
|
||||||
ErrNilValidatorAddr = types.ErrNilValidatorAddr
|
ErrNilValidatorAddr = types.ErrNilValidatorAddr
|
||||||
ErrBadValidatorAddr = types.ErrBadValidatorAddr
|
ErrBadValidatorAddr = types.ErrBadValidatorAddr
|
||||||
ErrNoValidatorFound = types.ErrNoValidatorFound
|
ErrNoValidatorFound = types.ErrNoValidatorFound
|
||||||
|
@ -131,6 +137,8 @@ var (
|
||||||
ErrBothShareMsgsGiven = types.ErrBothShareMsgsGiven
|
ErrBothShareMsgsGiven = types.ErrBothShareMsgsGiven
|
||||||
ErrNeitherShareMsgsGiven = types.ErrNeitherShareMsgsGiven
|
ErrNeitherShareMsgsGiven = types.ErrNeitherShareMsgsGiven
|
||||||
ErrMissingSignature = types.ErrMissingSignature
|
ErrMissingSignature = types.ErrMissingSignature
|
||||||
|
ErrInvalidHistoricalInfo = types.ErrInvalidHistoricalInfo
|
||||||
|
ErrNoHistoricalInfo = types.ErrNoHistoricalInfo
|
||||||
NewGenesisState = types.NewGenesisState
|
NewGenesisState = types.NewGenesisState
|
||||||
DefaultGenesisState = types.DefaultGenesisState
|
DefaultGenesisState = types.DefaultGenesisState
|
||||||
NewMultiStakingHooks = types.NewMultiStakingHooks
|
NewMultiStakingHooks = types.NewMultiStakingHooks
|
||||||
|
@ -159,6 +167,7 @@ var (
|
||||||
GetREDsFromValSrcIndexKey = types.GetREDsFromValSrcIndexKey
|
GetREDsFromValSrcIndexKey = types.GetREDsFromValSrcIndexKey
|
||||||
GetREDsToValDstIndexKey = types.GetREDsToValDstIndexKey
|
GetREDsToValDstIndexKey = types.GetREDsToValDstIndexKey
|
||||||
GetREDsByDelToValDstIndexKey = types.GetREDsByDelToValDstIndexKey
|
GetREDsByDelToValDstIndexKey = types.GetREDsByDelToValDstIndexKey
|
||||||
|
GetHistoricalInfoKey = types.GetHistoricalInfoKey
|
||||||
NewMsgCreateValidator = types.NewMsgCreateValidator
|
NewMsgCreateValidator = types.NewMsgCreateValidator
|
||||||
NewMsgEditValidator = types.NewMsgEditValidator
|
NewMsgEditValidator = types.NewMsgEditValidator
|
||||||
NewMsgDelegate = types.NewMsgDelegate
|
NewMsgDelegate = types.NewMsgDelegate
|
||||||
|
@ -174,6 +183,7 @@ var (
|
||||||
NewQueryBondsParams = types.NewQueryBondsParams
|
NewQueryBondsParams = types.NewQueryBondsParams
|
||||||
NewQueryRedelegationParams = types.NewQueryRedelegationParams
|
NewQueryRedelegationParams = types.NewQueryRedelegationParams
|
||||||
NewQueryValidatorsParams = types.NewQueryValidatorsParams
|
NewQueryValidatorsParams = types.NewQueryValidatorsParams
|
||||||
|
NewQueryHistoricalInfoParams = types.NewQueryHistoricalInfoParams
|
||||||
NewValidator = types.NewValidator
|
NewValidator = types.NewValidator
|
||||||
MustMarshalValidator = types.MustMarshalValidator
|
MustMarshalValidator = types.MustMarshalValidator
|
||||||
MustUnmarshalValidator = types.MustUnmarshalValidator
|
MustUnmarshalValidator = types.MustUnmarshalValidator
|
||||||
|
@ -196,6 +206,7 @@ var (
|
||||||
UnbondingQueueKey = types.UnbondingQueueKey
|
UnbondingQueueKey = types.UnbondingQueueKey
|
||||||
RedelegationQueueKey = types.RedelegationQueueKey
|
RedelegationQueueKey = types.RedelegationQueueKey
|
||||||
ValidatorQueueKey = types.ValidatorQueueKey
|
ValidatorQueueKey = types.ValidatorQueueKey
|
||||||
|
HistoricalInfoKey = types.HistoricalInfoKey
|
||||||
KeyUnbondingTime = types.KeyUnbondingTime
|
KeyUnbondingTime = types.KeyUnbondingTime
|
||||||
KeyMaxValidators = types.KeyMaxValidators
|
KeyMaxValidators = types.KeyMaxValidators
|
||||||
KeyMaxEntries = types.KeyMaxEntries
|
KeyMaxEntries = types.KeyMaxEntries
|
||||||
|
@ -216,6 +227,7 @@ type (
|
||||||
Redelegation = types.Redelegation
|
Redelegation = types.Redelegation
|
||||||
RedelegationEntry = types.RedelegationEntry
|
RedelegationEntry = types.RedelegationEntry
|
||||||
Redelegations = types.Redelegations
|
Redelegations = types.Redelegations
|
||||||
|
HistoricalInfo = types.HistoricalInfo
|
||||||
DelegationResponse = types.DelegationResponse
|
DelegationResponse = types.DelegationResponse
|
||||||
DelegationResponses = types.DelegationResponses
|
DelegationResponses = types.DelegationResponses
|
||||||
RedelegationResponse = types.RedelegationResponse
|
RedelegationResponse = types.RedelegationResponse
|
||||||
|
@ -237,6 +249,7 @@ type (
|
||||||
QueryBondsParams = types.QueryBondsParams
|
QueryBondsParams = types.QueryBondsParams
|
||||||
QueryRedelegationParams = types.QueryRedelegationParams
|
QueryRedelegationParams = types.QueryRedelegationParams
|
||||||
QueryValidatorsParams = types.QueryValidatorsParams
|
QueryValidatorsParams = types.QueryValidatorsParams
|
||||||
|
QueryHistoricalInfoParams = types.QueryHistoricalInfoParams
|
||||||
Validator = types.Validator
|
Validator = types.Validator
|
||||||
Validators = types.Validators
|
Validators = types.Validators
|
||||||
Description = types.Description
|
Description = types.Description
|
||||||
|
|
|
@ -2,6 +2,7 @@ package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -35,6 +36,7 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||||
GetCmdQueryValidatorDelegations(queryRoute, cdc),
|
GetCmdQueryValidatorDelegations(queryRoute, cdc),
|
||||||
GetCmdQueryValidatorUnbondingDelegations(queryRoute, cdc),
|
GetCmdQueryValidatorUnbondingDelegations(queryRoute, cdc),
|
||||||
GetCmdQueryValidatorRedelegations(queryRoute, cdc),
|
GetCmdQueryValidatorRedelegations(queryRoute, cdc),
|
||||||
|
GetCmdQueryHistoricalInfo(queryRoute, cdc),
|
||||||
GetCmdQueryParams(queryRoute, cdc),
|
GetCmdQueryParams(queryRoute, cdc),
|
||||||
GetCmdQueryPool(queryRoute, cdc))...)
|
GetCmdQueryPool(queryRoute, cdc))...)
|
||||||
|
|
||||||
|
@ -527,6 +529,50 @@ $ %s query staking redelegation cosmos1gghjut3ccd8ay0zduzj64hwre2fxs9ld75ru9p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCmdQueryHistoricalInfo implements the historical info query command
|
||||||
|
func GetCmdQueryHistoricalInfo(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "historical-info [height]",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
Short: "Query historical info at given height",
|
||||||
|
Long: strings.TrimSpace(
|
||||||
|
fmt.Sprintf(`Query historical info at given height.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
$ %s query staking historical-info 5
|
||||||
|
`,
|
||||||
|
version.ClientName,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||||
|
|
||||||
|
height, err := strconv.ParseInt(args[0], 10, 64)
|
||||||
|
if err != nil || height < 0 {
|
||||||
|
return fmt.Errorf("height argument provided must be a non-negative-integer: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bz, err := cdc.MarshalJSON(types.QueryHistoricalInfoParams{Height: height})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryHistoricalInfo)
|
||||||
|
res, _, err := cliCtx.QueryWithData(route, bz)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp types.HistoricalInfo
|
||||||
|
if err := cdc.UnmarshalJSON(res, &resp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cliCtx.PrintOutput(resp)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GetCmdQueryPool implements the pool query command.
|
// GetCmdQueryPool implements the pool query command.
|
||||||
func GetCmdQueryPool(storeName string, cdc *codec.Codec) *cobra.Command {
|
func GetCmdQueryPool(storeName string, cdc *codec.Codec) *cobra.Command {
|
||||||
return &cobra.Command{
|
return &cobra.Command{
|
||||||
|
|
|
@ -3,6 +3,7 @@ package rest
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
@ -86,6 +87,12 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) {
|
||||||
validatorUnbondingDelegationsHandlerFn(cliCtx),
|
validatorUnbondingDelegationsHandlerFn(cliCtx),
|
||||||
).Methods("GET")
|
).Methods("GET")
|
||||||
|
|
||||||
|
// Get HistoricalInfo at a given height
|
||||||
|
r.HandleFunc(
|
||||||
|
"/staking/historical_info/{height}",
|
||||||
|
historicalInfoHandlerFn(cliCtx),
|
||||||
|
).Methods("GET")
|
||||||
|
|
||||||
// Get the current state of the staking pool
|
// Get the current state of the staking pool
|
||||||
r.HandleFunc(
|
r.HandleFunc(
|
||||||
"/staking/pool",
|
"/staking/pool",
|
||||||
|
@ -313,6 +320,36 @@ func validatorUnbondingDelegationsHandlerFn(cliCtx context.CLIContext) http.Hand
|
||||||
return queryValidator(cliCtx, "custom/staking/validatorUnbondingDelegations")
|
return queryValidator(cliCtx, "custom/staking/validatorUnbondingDelegations")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTTP request handler to query historical info at a given height
|
||||||
|
func historicalInfoHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
heightStr := vars["height"]
|
||||||
|
height, err := strconv.ParseInt(heightStr, 10, 64)
|
||||||
|
if err != nil || height < 0 {
|
||||||
|
rest.WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("Must provide non-negative integer for height: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
params := types.NewQueryHistoricalInfoParams(height)
|
||||||
|
bz, err := cliCtx.Codec.MarshalJSON(params)
|
||||||
|
if err != nil {
|
||||||
|
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryHistoricalInfo)
|
||||||
|
res, height, err := cliCtx.QueryWithData(route, bz)
|
||||||
|
if err != nil {
|
||||||
|
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cliCtx = cliCtx.WithHeight(height)
|
||||||
|
rest.PostProcessResponse(w, cliCtx, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// HTTP request handler to query the pool information
|
// HTTP request handler to query the pool information
|
||||||
func poolHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
|
func poolHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
"github.com/tendermint/tendermint/libs/common"
|
"github.com/tendermint/tendermint/libs/common"
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
|
|
||||||
|
@ -40,61 +39,6 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called every block, update validator set
|
|
||||||
func EndBlocker(ctx sdk.Context, k keeper.Keeper) []abci.ValidatorUpdate {
|
|
||||||
// Calculate validator set changes.
|
|
||||||
//
|
|
||||||
// NOTE: ApplyAndReturnValidatorSetUpdates has to come before
|
|
||||||
// UnbondAllMatureValidatorQueue.
|
|
||||||
// This fixes a bug when the unbonding period is instant (is the case in
|
|
||||||
// some of the tests). The test expected the validator to be completely
|
|
||||||
// unbonded after the Endblocker (go from Bonded -> Unbonding during
|
|
||||||
// ApplyAndReturnValidatorSetUpdates and then Unbonding -> Unbonded during
|
|
||||||
// UnbondAllMatureValidatorQueue).
|
|
||||||
validatorUpdates := k.ApplyAndReturnValidatorSetUpdates(ctx)
|
|
||||||
|
|
||||||
// Unbond all mature validators from the unbonding queue.
|
|
||||||
k.UnbondAllMatureValidatorQueue(ctx)
|
|
||||||
|
|
||||||
// Remove all mature unbonding delegations from the ubd queue.
|
|
||||||
matureUnbonds := k.DequeueAllMatureUBDQueue(ctx, ctx.BlockHeader().Time)
|
|
||||||
for _, dvPair := range matureUnbonds {
|
|
||||||
err := k.CompleteUnbonding(ctx, dvPair.DelegatorAddress, dvPair.ValidatorAddress)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.EventManager().EmitEvent(
|
|
||||||
sdk.NewEvent(
|
|
||||||
types.EventTypeCompleteUnbonding,
|
|
||||||
sdk.NewAttribute(types.AttributeKeyValidator, dvPair.ValidatorAddress.String()),
|
|
||||||
sdk.NewAttribute(types.AttributeKeyDelegator, dvPair.DelegatorAddress.String()),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove all mature redelegations from the red queue.
|
|
||||||
matureRedelegations := k.DequeueAllMatureRedelegationQueue(ctx, ctx.BlockHeader().Time)
|
|
||||||
for _, dvvTriplet := range matureRedelegations {
|
|
||||||
err := k.CompleteRedelegation(ctx, dvvTriplet.DelegatorAddress,
|
|
||||||
dvvTriplet.ValidatorSrcAddress, dvvTriplet.ValidatorDstAddress)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.EventManager().EmitEvent(
|
|
||||||
sdk.NewEvent(
|
|
||||||
types.EventTypeCompleteRedelegation,
|
|
||||||
sdk.NewAttribute(types.AttributeKeyDelegator, dvvTriplet.DelegatorAddress.String()),
|
|
||||||
sdk.NewAttribute(types.AttributeKeySrcValidator, dvvTriplet.ValidatorSrcAddress.String()),
|
|
||||||
sdk.NewAttribute(types.AttributeKeyDstValidator, dvvTriplet.ValidatorDstAddress.String()),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return validatorUpdates
|
|
||||||
}
|
|
||||||
|
|
||||||
// These functions assume everything has been authenticated,
|
// These functions assume everything has been authenticated,
|
||||||
// now we just perform action and save
|
// now we just perform action and save
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
package keeper
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetHistoricalInfo gets the historical info at a given height
|
||||||
|
func (k Keeper) GetHistoricalInfo(ctx sdk.Context, height int64) (types.HistoricalInfo, bool) {
|
||||||
|
store := ctx.KVStore(k.storeKey)
|
||||||
|
key := types.GetHistoricalInfoKey(height)
|
||||||
|
|
||||||
|
value := store.Get(key)
|
||||||
|
if value == nil {
|
||||||
|
return types.HistoricalInfo{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
hi := types.MustUnmarshalHistoricalInfo(k.cdc, value)
|
||||||
|
return hi, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHistoricalInfo sets the historical info at a given height
|
||||||
|
func (k Keeper) SetHistoricalInfo(ctx sdk.Context, height int64, hi types.HistoricalInfo) {
|
||||||
|
store := ctx.KVStore(k.storeKey)
|
||||||
|
key := types.GetHistoricalInfoKey(height)
|
||||||
|
|
||||||
|
value := types.MustMarshalHistoricalInfo(k.cdc, hi)
|
||||||
|
store.Set(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteHistoricalInfo deletes the historical info at a given height
|
||||||
|
func (k Keeper) DeleteHistoricalInfo(ctx sdk.Context, height int64) {
|
||||||
|
store := ctx.KVStore(k.storeKey)
|
||||||
|
key := types.GetHistoricalInfoKey(height)
|
||||||
|
|
||||||
|
store.Delete(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrackHistoricalInfo saves the latest historical-info and deletes the oldest
|
||||||
|
// heights that are below pruning height
|
||||||
|
func (k Keeper) TrackHistoricalInfo(ctx sdk.Context) {
|
||||||
|
entryNum := k.HistoricalEntries(ctx)
|
||||||
|
|
||||||
|
// Prune store to ensure we only have parameter-defined historical entries.
|
||||||
|
// In most cases, this will involve removing a single historical entry.
|
||||||
|
// In the rare scenario when the historical entries gets reduced to a lower value k'
|
||||||
|
// from the original value k. k - k' entries must be deleted from the store.
|
||||||
|
// Since the entries to be deleted are always in a continuous range, we can iterate
|
||||||
|
// over the historical entries starting from the most recent version to be pruned
|
||||||
|
// and then return at the first empty entry.
|
||||||
|
for i := ctx.BlockHeight() - int64(entryNum); i >= 0; i-- {
|
||||||
|
_, found := k.GetHistoricalInfo(ctx, i)
|
||||||
|
if found {
|
||||||
|
k.DeleteHistoricalInfo(ctx, i)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there is no need to persist historicalInfo, return
|
||||||
|
if entryNum == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create HistoricalInfo struct
|
||||||
|
lastVals := k.GetLastValidators(ctx)
|
||||||
|
historicalEntry := types.NewHistoricalInfo(ctx.BlockHeader(), lastVals)
|
||||||
|
|
||||||
|
// Set latest HistoricalInfo at current height
|
||||||
|
k.SetHistoricalInfo(ctx, ctx.BlockHeight(), historicalEntry)
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
package keeper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHistoricalInfo(t *testing.T) {
|
||||||
|
ctx, _, keeper, _ := CreateTestInput(t, false, 10)
|
||||||
|
validators := make([]types.Validator, len(addrVals))
|
||||||
|
|
||||||
|
for i, valAddr := range addrVals {
|
||||||
|
validators[i] = types.NewValidator(valAddr, PKs[i], types.Description{})
|
||||||
|
}
|
||||||
|
|
||||||
|
hi := types.NewHistoricalInfo(ctx.BlockHeader(), validators)
|
||||||
|
|
||||||
|
keeper.SetHistoricalInfo(ctx, 2, hi)
|
||||||
|
|
||||||
|
recv, found := keeper.GetHistoricalInfo(ctx, 2)
|
||||||
|
require.True(t, found, "HistoricalInfo not found after set")
|
||||||
|
require.Equal(t, hi, recv, "HistoricalInfo not equal")
|
||||||
|
require.True(t, sort.IsSorted(types.Validators(recv.ValSet)), "HistoricalInfo validators is not sorted")
|
||||||
|
|
||||||
|
keeper.DeleteHistoricalInfo(ctx, 2)
|
||||||
|
|
||||||
|
recv, found = keeper.GetHistoricalInfo(ctx, 2)
|
||||||
|
require.False(t, found, "HistoricalInfo found after delete")
|
||||||
|
require.Equal(t, types.HistoricalInfo{}, recv, "HistoricalInfo is not empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTrackHistoricalInfo(t *testing.T) {
|
||||||
|
ctx, _, k, _ := CreateTestInput(t, false, 10)
|
||||||
|
|
||||||
|
// set historical entries in params to 5
|
||||||
|
params := types.DefaultParams()
|
||||||
|
params.HistoricalEntries = 5
|
||||||
|
k.SetParams(ctx, params)
|
||||||
|
|
||||||
|
// set historical info at 5, 4 which should be pruned
|
||||||
|
// and check that it has been stored
|
||||||
|
h4 := abci.Header{
|
||||||
|
ChainID: "HelloChain",
|
||||||
|
Height: 4,
|
||||||
|
}
|
||||||
|
h5 := abci.Header{
|
||||||
|
ChainID: "HelloChain",
|
||||||
|
Height: 5,
|
||||||
|
}
|
||||||
|
valSet := []types.Validator{
|
||||||
|
types.NewValidator(sdk.ValAddress(Addrs[0]), PKs[0], types.Description{}),
|
||||||
|
types.NewValidator(sdk.ValAddress(Addrs[1]), PKs[1], types.Description{}),
|
||||||
|
}
|
||||||
|
hi4 := types.NewHistoricalInfo(h4, valSet)
|
||||||
|
hi5 := types.NewHistoricalInfo(h5, valSet)
|
||||||
|
k.SetHistoricalInfo(ctx, 4, hi4)
|
||||||
|
k.SetHistoricalInfo(ctx, 5, hi5)
|
||||||
|
recv, found := k.GetHistoricalInfo(ctx, 4)
|
||||||
|
require.True(t, found)
|
||||||
|
require.Equal(t, hi4, recv)
|
||||||
|
recv, found = k.GetHistoricalInfo(ctx, 5)
|
||||||
|
require.True(t, found)
|
||||||
|
require.Equal(t, hi5, recv)
|
||||||
|
|
||||||
|
// Set last validators in keeper
|
||||||
|
val1 := types.NewValidator(sdk.ValAddress(Addrs[2]), PKs[2], types.Description{})
|
||||||
|
k.SetValidator(ctx, val1)
|
||||||
|
k.SetLastValidatorPower(ctx, val1.OperatorAddress, 10)
|
||||||
|
val2 := types.NewValidator(sdk.ValAddress(Addrs[3]), PKs[3], types.Description{})
|
||||||
|
vals := []types.Validator{val1, val2}
|
||||||
|
sort.Sort(types.Validators(vals))
|
||||||
|
k.SetValidator(ctx, val2)
|
||||||
|
k.SetLastValidatorPower(ctx, val2.OperatorAddress, 8)
|
||||||
|
|
||||||
|
// Set Header for BeginBlock context
|
||||||
|
header := abci.Header{
|
||||||
|
ChainID: "HelloChain",
|
||||||
|
Height: 10,
|
||||||
|
}
|
||||||
|
ctx = ctx.WithBlockHeader(header)
|
||||||
|
|
||||||
|
k.TrackHistoricalInfo(ctx)
|
||||||
|
|
||||||
|
// Check HistoricalInfo at height 10 is persisted
|
||||||
|
expected := types.HistoricalInfo{
|
||||||
|
Header: header,
|
||||||
|
ValSet: vals,
|
||||||
|
}
|
||||||
|
recv, found = k.GetHistoricalInfo(ctx, 10)
|
||||||
|
require.True(t, found, "GetHistoricalInfo failed after BeginBlock")
|
||||||
|
require.Equal(t, expected, recv, "GetHistoricalInfo returned eunexpected result")
|
||||||
|
|
||||||
|
// Check HistoricalInfo at height 5, 4 is pruned
|
||||||
|
recv, found = k.GetHistoricalInfo(ctx, 4)
|
||||||
|
require.False(t, found, "GetHistoricalInfo did not prune earlier height")
|
||||||
|
require.Equal(t, types.HistoricalInfo{}, recv, "GetHistoricalInfo at height 4 is not empty after prune")
|
||||||
|
recv, found = k.GetHistoricalInfo(ctx, 5)
|
||||||
|
require.False(t, found, "GetHistoricalInfo did not prune first prune height")
|
||||||
|
require.Equal(t, types.HistoricalInfo{}, recv, "GetHistoricalInfo at height 5 is not empty after prune")
|
||||||
|
}
|
|
@ -37,6 +37,13 @@ func (k Keeper) MaxEntries(ctx sdk.Context) (res uint16) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HistoricalEntries = number of historical info entries
|
||||||
|
// to persist in store
|
||||||
|
func (k Keeper) HistoricalEntries(ctx sdk.Context) (res uint16) {
|
||||||
|
k.paramstore.Get(ctx, types.KeyHistoricalEntries, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// BondDenom - Bondable coin denomination
|
// BondDenom - Bondable coin denomination
|
||||||
func (k Keeper) BondDenom(ctx sdk.Context) (res string) {
|
func (k Keeper) BondDenom(ctx sdk.Context) (res string) {
|
||||||
k.paramstore.Get(ctx, types.KeyBondDenom, &res)
|
k.paramstore.Get(ctx, types.KeyBondDenom, &res)
|
||||||
|
@ -49,6 +56,7 @@ func (k Keeper) GetParams(ctx sdk.Context) types.Params {
|
||||||
k.UnbondingTime(ctx),
|
k.UnbondingTime(ctx),
|
||||||
k.MaxValidators(ctx),
|
k.MaxValidators(ctx),
|
||||||
k.MaxEntries(ctx),
|
k.MaxEntries(ctx),
|
||||||
|
k.HistoricalEntries(ctx),
|
||||||
k.BondDenom(ctx),
|
k.BondDenom(ctx),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,8 @@ func NewQuerier(k Keeper) sdk.Querier {
|
||||||
return queryDelegatorValidators(ctx, req, k)
|
return queryDelegatorValidators(ctx, req, k)
|
||||||
case types.QueryDelegatorValidator:
|
case types.QueryDelegatorValidator:
|
||||||
return queryDelegatorValidator(ctx, req, k)
|
return queryDelegatorValidator(ctx, req, k)
|
||||||
|
case types.QueryHistoricalInfo:
|
||||||
|
return queryHistoricalInfo(ctx, req, k)
|
||||||
case types.QueryPool:
|
case types.QueryPool:
|
||||||
return queryPool(ctx, k)
|
return queryPool(ctx, k)
|
||||||
case types.QueryParameters:
|
case types.QueryParameters:
|
||||||
|
@ -327,6 +329,27 @@ func queryRedelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byt
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func queryHistoricalInfo(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) {
|
||||||
|
var params types.QueryHistoricalInfoParams
|
||||||
|
|
||||||
|
err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms)
|
||||||
|
if err != nil {
|
||||||
|
return nil, sdk.ErrUnknownRequest(string(req.Data))
|
||||||
|
}
|
||||||
|
|
||||||
|
hi, found := k.GetHistoricalInfo(ctx, params.Height)
|
||||||
|
if !found {
|
||||||
|
return nil, types.ErrNoHistoricalInfo(types.DefaultCodespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := codec.MarshalJSONIndent(types.ModuleCdc, hi)
|
||||||
|
if err != nil {
|
||||||
|
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
func queryPool(ctx sdk.Context, k Keeper) ([]byte, sdk.Error) {
|
func queryPool(ctx sdk.Context, k Keeper) ([]byte, sdk.Error) {
|
||||||
bondDenom := k.BondDenom(ctx)
|
bondDenom := k.BondDenom(ctx)
|
||||||
bondedPool := k.GetBondedPool(ctx)
|
bondedPool := k.GetBondedPool(ctx)
|
||||||
|
|
|
@ -31,6 +31,13 @@ func TestNewQuerier(t *testing.T) {
|
||||||
keeper.SetValidatorByPowerIndex(ctx, validators[i])
|
keeper.SetValidatorByPowerIndex(ctx, validators[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
header := abci.Header{
|
||||||
|
ChainID: "HelloChain",
|
||||||
|
Height: 5,
|
||||||
|
}
|
||||||
|
hi := types.NewHistoricalInfo(header, validators[:])
|
||||||
|
keeper.SetHistoricalInfo(ctx, 5, hi)
|
||||||
|
|
||||||
query := abci.RequestQuery{
|
query := abci.RequestQuery{
|
||||||
Path: "",
|
Path: "",
|
||||||
Data: []byte{},
|
Data: []byte{},
|
||||||
|
@ -39,53 +46,63 @@ func TestNewQuerier(t *testing.T) {
|
||||||
querier := NewQuerier(keeper)
|
querier := NewQuerier(keeper)
|
||||||
|
|
||||||
bz, err := querier(ctx, []string{"other"}, query)
|
bz, err := querier(ctx, []string{"other"}, query)
|
||||||
require.NotNil(t, err)
|
require.Error(t, err)
|
||||||
require.Nil(t, bz)
|
require.Nil(t, bz)
|
||||||
|
|
||||||
_, err = querier(ctx, []string{"pool"}, query)
|
_, err = querier(ctx, []string{"pool"}, query)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = querier(ctx, []string{"parameters"}, query)
|
_, err = querier(ctx, []string{"parameters"}, query)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
queryValParams := types.NewQueryValidatorParams(addrVal1)
|
queryValParams := types.NewQueryValidatorParams(addrVal1)
|
||||||
bz, errRes := cdc.MarshalJSON(queryValParams)
|
bz, errRes := cdc.MarshalJSON(queryValParams)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
|
|
||||||
query.Path = "/custom/staking/validator"
|
query.Path = "/custom/staking/validator"
|
||||||
query.Data = bz
|
query.Data = bz
|
||||||
|
|
||||||
_, err = querier(ctx, []string{"validator"}, query)
|
_, err = querier(ctx, []string{"validator"}, query)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = querier(ctx, []string{"validatorDelegations"}, query)
|
_, err = querier(ctx, []string{"validatorDelegations"}, query)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = querier(ctx, []string{"validatorUnbondingDelegations"}, query)
|
_, err = querier(ctx, []string{"validatorUnbondingDelegations"}, query)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
queryDelParams := types.NewQueryDelegatorParams(addrAcc2)
|
queryDelParams := types.NewQueryDelegatorParams(addrAcc2)
|
||||||
bz, errRes = cdc.MarshalJSON(queryDelParams)
|
bz, errRes = cdc.MarshalJSON(queryDelParams)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
|
|
||||||
query.Path = "/custom/staking/validator"
|
query.Path = "/custom/staking/validator"
|
||||||
query.Data = bz
|
query.Data = bz
|
||||||
|
|
||||||
_, err = querier(ctx, []string{"delegatorDelegations"}, query)
|
_, err = querier(ctx, []string{"delegatorDelegations"}, query)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = querier(ctx, []string{"delegatorUnbondingDelegations"}, query)
|
_, err = querier(ctx, []string{"delegatorUnbondingDelegations"}, query)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, err = querier(ctx, []string{"delegatorValidators"}, query)
|
_, err = querier(ctx, []string{"delegatorValidators"}, query)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
bz, errRes = cdc.MarshalJSON(types.NewQueryRedelegationParams(nil, nil, nil))
|
bz, errRes = cdc.MarshalJSON(types.NewQueryRedelegationParams(nil, nil, nil))
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
query.Data = bz
|
query.Data = bz
|
||||||
|
|
||||||
_, err = querier(ctx, []string{"redelegations"}, query)
|
_, err = querier(ctx, []string{"redelegations"}, query)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
queryHisParams := types.NewQueryHistoricalInfoParams(5)
|
||||||
|
bz, errRes = cdc.MarshalJSON(queryHisParams)
|
||||||
|
require.NoError(t, errRes)
|
||||||
|
|
||||||
|
query.Path = "/custom/staking/historicalInfo"
|
||||||
|
query.Data = bz
|
||||||
|
|
||||||
|
_, err = querier(ctx, []string{"historicalInfo"}, query)
|
||||||
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestQueryParametersPool(t *testing.T) {
|
func TestQueryParametersPool(t *testing.T) {
|
||||||
|
@ -94,21 +111,21 @@ func TestQueryParametersPool(t *testing.T) {
|
||||||
bondDenom := sdk.DefaultBondDenom
|
bondDenom := sdk.DefaultBondDenom
|
||||||
|
|
||||||
res, err := queryParameters(ctx, keeper)
|
res, err := queryParameters(ctx, keeper)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var params types.Params
|
var params types.Params
|
||||||
errRes := cdc.UnmarshalJSON(res, ¶ms)
|
errRes := cdc.UnmarshalJSON(res, ¶ms)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
require.Equal(t, keeper.GetParams(ctx), params)
|
require.Equal(t, keeper.GetParams(ctx), params)
|
||||||
|
|
||||||
res, err = queryPool(ctx, keeper)
|
res, err = queryPool(ctx, keeper)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var pool types.Pool
|
var pool types.Pool
|
||||||
bondedPool := keeper.GetBondedPool(ctx)
|
bondedPool := keeper.GetBondedPool(ctx)
|
||||||
notBondedPool := keeper.GetNotBondedPool(ctx)
|
notBondedPool := keeper.GetNotBondedPool(ctx)
|
||||||
errRes = cdc.UnmarshalJSON(res, &pool)
|
errRes = cdc.UnmarshalJSON(res, &pool)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
require.Equal(t, bondedPool.GetCoins().AmountOf(bondDenom), pool.BondedTokens)
|
require.Equal(t, bondedPool.GetCoins().AmountOf(bondDenom), pool.BondedTokens)
|
||||||
require.Equal(t, notBondedPool.GetCoins().AmountOf(bondDenom), pool.NotBondedTokens)
|
require.Equal(t, notBondedPool.GetCoins().AmountOf(bondDenom), pool.NotBondedTokens)
|
||||||
}
|
}
|
||||||
|
@ -138,7 +155,7 @@ func TestQueryValidators(t *testing.T) {
|
||||||
for i, s := range status {
|
for i, s := range status {
|
||||||
queryValsParams := types.NewQueryValidatorsParams(1, int(params.MaxValidators), s.String())
|
queryValsParams := types.NewQueryValidatorsParams(1, int(params.MaxValidators), s.String())
|
||||||
bz, err := cdc.MarshalJSON(queryValsParams)
|
bz, err := cdc.MarshalJSON(queryValsParams)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
req := abci.RequestQuery{
|
req := abci.RequestQuery{
|
||||||
Path: fmt.Sprintf("/custom/%s/%s", types.QuerierRoute, types.QueryValidators),
|
Path: fmt.Sprintf("/custom/%s/%s", types.QuerierRoute, types.QueryValidators),
|
||||||
|
@ -146,11 +163,11 @@ func TestQueryValidators(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := queryValidators(ctx, req, keeper)
|
res, err := queryValidators(ctx, req, keeper)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var validatorsResp []types.Validator
|
var validatorsResp []types.Validator
|
||||||
err = cdc.UnmarshalJSON(res, &validatorsResp)
|
err = cdc.UnmarshalJSON(res, &validatorsResp)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, 1, len(validatorsResp))
|
require.Equal(t, 1, len(validatorsResp))
|
||||||
require.ElementsMatch(t, validators[i].OperatorAddress, validatorsResp[0].OperatorAddress)
|
require.ElementsMatch(t, validators[i].OperatorAddress, validatorsResp[0].OperatorAddress)
|
||||||
|
@ -160,18 +177,18 @@ func TestQueryValidators(t *testing.T) {
|
||||||
// Query each validator
|
// Query each validator
|
||||||
queryParams := types.NewQueryValidatorParams(addrVal1)
|
queryParams := types.NewQueryValidatorParams(addrVal1)
|
||||||
bz, err := cdc.MarshalJSON(queryParams)
|
bz, err := cdc.MarshalJSON(queryParams)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
query := abci.RequestQuery{
|
query := abci.RequestQuery{
|
||||||
Path: "/custom/staking/validator",
|
Path: "/custom/staking/validator",
|
||||||
Data: bz,
|
Data: bz,
|
||||||
}
|
}
|
||||||
res, err := queryValidator(ctx, query, keeper)
|
res, err := queryValidator(ctx, query, keeper)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var validator types.Validator
|
var validator types.Validator
|
||||||
err = cdc.UnmarshalJSON(res, &validator)
|
err = cdc.UnmarshalJSON(res, &validator)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, queriedValidators[0], validator)
|
require.Equal(t, queriedValidators[0], validator)
|
||||||
}
|
}
|
||||||
|
@ -199,7 +216,7 @@ func TestQueryDelegation(t *testing.T) {
|
||||||
// Query Delegator bonded validators
|
// Query Delegator bonded validators
|
||||||
queryParams := types.NewQueryDelegatorParams(addrAcc2)
|
queryParams := types.NewQueryDelegatorParams(addrAcc2)
|
||||||
bz, errRes := cdc.MarshalJSON(queryParams)
|
bz, errRes := cdc.MarshalJSON(queryParams)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
|
|
||||||
query := abci.RequestQuery{
|
query := abci.RequestQuery{
|
||||||
Path: "/custom/staking/delegatorValidators",
|
Path: "/custom/staking/delegatorValidators",
|
||||||
|
@ -209,11 +226,11 @@ func TestQueryDelegation(t *testing.T) {
|
||||||
delValidators := keeper.GetDelegatorValidators(ctx, addrAcc2, params.MaxValidators)
|
delValidators := keeper.GetDelegatorValidators(ctx, addrAcc2, params.MaxValidators)
|
||||||
|
|
||||||
res, err := queryDelegatorValidators(ctx, query, keeper)
|
res, err := queryDelegatorValidators(ctx, query, keeper)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var validatorsResp []types.Validator
|
var validatorsResp []types.Validator
|
||||||
errRes = cdc.UnmarshalJSON(res, &validatorsResp)
|
errRes = cdc.UnmarshalJSON(res, &validatorsResp)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
|
|
||||||
require.Equal(t, len(delValidators), len(validatorsResp))
|
require.Equal(t, len(delValidators), len(validatorsResp))
|
||||||
require.ElementsMatch(t, delValidators, validatorsResp)
|
require.ElementsMatch(t, delValidators, validatorsResp)
|
||||||
|
@ -222,12 +239,12 @@ func TestQueryDelegation(t *testing.T) {
|
||||||
query.Data = bz[:len(bz)-1]
|
query.Data = bz[:len(bz)-1]
|
||||||
|
|
||||||
_, err = queryDelegatorValidators(ctx, query, keeper)
|
_, err = queryDelegatorValidators(ctx, query, keeper)
|
||||||
require.NotNil(t, err)
|
require.Error(t, err)
|
||||||
|
|
||||||
// Query bonded validator
|
// Query bonded validator
|
||||||
queryBondParams := types.NewQueryBondsParams(addrAcc2, addrVal1)
|
queryBondParams := types.NewQueryBondsParams(addrAcc2, addrVal1)
|
||||||
bz, errRes = cdc.MarshalJSON(queryBondParams)
|
bz, errRes = cdc.MarshalJSON(queryBondParams)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
|
|
||||||
query = abci.RequestQuery{
|
query = abci.RequestQuery{
|
||||||
Path: "/custom/staking/delegatorValidator",
|
Path: "/custom/staking/delegatorValidator",
|
||||||
|
@ -235,11 +252,11 @@ func TestQueryDelegation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err = queryDelegatorValidator(ctx, query, keeper)
|
res, err = queryDelegatorValidator(ctx, query, keeper)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var validator types.Validator
|
var validator types.Validator
|
||||||
errRes = cdc.UnmarshalJSON(res, &validator)
|
errRes = cdc.UnmarshalJSON(res, &validator)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
|
|
||||||
require.Equal(t, delValidators[0], validator)
|
require.Equal(t, delValidators[0], validator)
|
||||||
|
|
||||||
|
@ -247,7 +264,7 @@ func TestQueryDelegation(t *testing.T) {
|
||||||
query.Data = bz[:len(bz)-1]
|
query.Data = bz[:len(bz)-1]
|
||||||
|
|
||||||
_, err = queryDelegatorValidator(ctx, query, keeper)
|
_, err = queryDelegatorValidator(ctx, query, keeper)
|
||||||
require.NotNil(t, err)
|
require.Error(t, err)
|
||||||
|
|
||||||
// Query delegation
|
// Query delegation
|
||||||
|
|
||||||
|
@ -260,11 +277,11 @@ func TestQueryDelegation(t *testing.T) {
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
|
|
||||||
res, err = queryDelegation(ctx, query, keeper)
|
res, err = queryDelegation(ctx, query, keeper)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var delegationRes types.DelegationResponse
|
var delegationRes types.DelegationResponse
|
||||||
errRes = cdc.UnmarshalJSON(res, &delegationRes)
|
errRes = cdc.UnmarshalJSON(res, &delegationRes)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
|
|
||||||
require.Equal(t, delegation.ValidatorAddress, delegationRes.ValidatorAddress)
|
require.Equal(t, delegation.ValidatorAddress, delegationRes.ValidatorAddress)
|
||||||
require.Equal(t, delegation.DelegatorAddress, delegationRes.DelegatorAddress)
|
require.Equal(t, delegation.DelegatorAddress, delegationRes.DelegatorAddress)
|
||||||
|
@ -277,11 +294,11 @@ func TestQueryDelegation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err = queryDelegatorDelegations(ctx, query, keeper)
|
res, err = queryDelegatorDelegations(ctx, query, keeper)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var delegatorDelegations types.DelegationResponses
|
var delegatorDelegations types.DelegationResponses
|
||||||
errRes = cdc.UnmarshalJSON(res, &delegatorDelegations)
|
errRes = cdc.UnmarshalJSON(res, &delegatorDelegations)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
require.Len(t, delegatorDelegations, 1)
|
require.Len(t, delegatorDelegations, 1)
|
||||||
require.Equal(t, delegation.ValidatorAddress, delegatorDelegations[0].ValidatorAddress)
|
require.Equal(t, delegation.ValidatorAddress, delegatorDelegations[0].ValidatorAddress)
|
||||||
require.Equal(t, delegation.DelegatorAddress, delegatorDelegations[0].DelegatorAddress)
|
require.Equal(t, delegation.DelegatorAddress, delegatorDelegations[0].DelegatorAddress)
|
||||||
|
@ -291,12 +308,12 @@ func TestQueryDelegation(t *testing.T) {
|
||||||
query.Data = bz[:len(bz)-1]
|
query.Data = bz[:len(bz)-1]
|
||||||
|
|
||||||
_, err = queryDelegation(ctx, query, keeper)
|
_, err = queryDelegation(ctx, query, keeper)
|
||||||
require.NotNil(t, err)
|
require.Error(t, err)
|
||||||
|
|
||||||
// Query validator delegations
|
// Query validator delegations
|
||||||
|
|
||||||
bz, errRes = cdc.MarshalJSON(types.NewQueryValidatorParams(addrVal1))
|
bz, errRes = cdc.MarshalJSON(types.NewQueryValidatorParams(addrVal1))
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
|
|
||||||
query = abci.RequestQuery{
|
query = abci.RequestQuery{
|
||||||
Path: "custom/staking/validatorDelegations",
|
Path: "custom/staking/validatorDelegations",
|
||||||
|
@ -304,11 +321,11 @@ func TestQueryDelegation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err = queryValidatorDelegations(ctx, query, keeper)
|
res, err = queryValidatorDelegations(ctx, query, keeper)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var delegationsRes types.DelegationResponses
|
var delegationsRes types.DelegationResponses
|
||||||
errRes = cdc.UnmarshalJSON(res, &delegationsRes)
|
errRes = cdc.UnmarshalJSON(res, &delegationsRes)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
require.Len(t, delegatorDelegations, 1)
|
require.Len(t, delegatorDelegations, 1)
|
||||||
require.Equal(t, delegation.ValidatorAddress, delegationsRes[0].ValidatorAddress)
|
require.Equal(t, delegation.ValidatorAddress, delegationsRes[0].ValidatorAddress)
|
||||||
require.Equal(t, delegation.DelegatorAddress, delegationsRes[0].DelegatorAddress)
|
require.Equal(t, delegation.DelegatorAddress, delegationsRes[0].DelegatorAddress)
|
||||||
|
@ -317,11 +334,11 @@ func TestQueryDelegation(t *testing.T) {
|
||||||
// Query unbonging delegation
|
// Query unbonging delegation
|
||||||
unbondingTokens := sdk.TokensFromConsensusPower(10)
|
unbondingTokens := sdk.TokensFromConsensusPower(10)
|
||||||
_, err = keeper.Undelegate(ctx, addrAcc2, val1.OperatorAddress, unbondingTokens.ToDec())
|
_, err = keeper.Undelegate(ctx, addrAcc2, val1.OperatorAddress, unbondingTokens.ToDec())
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
queryBondParams = types.NewQueryBondsParams(addrAcc2, addrVal1)
|
queryBondParams = types.NewQueryBondsParams(addrAcc2, addrVal1)
|
||||||
bz, errRes = cdc.MarshalJSON(queryBondParams)
|
bz, errRes = cdc.MarshalJSON(queryBondParams)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
|
|
||||||
query = abci.RequestQuery{
|
query = abci.RequestQuery{
|
||||||
Path: "/custom/staking/unbondingDelegation",
|
Path: "/custom/staking/unbondingDelegation",
|
||||||
|
@ -332,11 +349,11 @@ func TestQueryDelegation(t *testing.T) {
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
|
|
||||||
res, err = queryUnbondingDelegation(ctx, query, keeper)
|
res, err = queryUnbondingDelegation(ctx, query, keeper)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var unbondRes types.UnbondingDelegation
|
var unbondRes types.UnbondingDelegation
|
||||||
errRes = cdc.UnmarshalJSON(res, &unbondRes)
|
errRes = cdc.UnmarshalJSON(res, &unbondRes)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
|
|
||||||
require.Equal(t, unbond, unbondRes)
|
require.Equal(t, unbond, unbondRes)
|
||||||
|
|
||||||
|
@ -344,7 +361,7 @@ func TestQueryDelegation(t *testing.T) {
|
||||||
query.Data = bz[:len(bz)-1]
|
query.Data = bz[:len(bz)-1]
|
||||||
|
|
||||||
_, err = queryUnbondingDelegation(ctx, query, keeper)
|
_, err = queryUnbondingDelegation(ctx, query, keeper)
|
||||||
require.NotNil(t, err)
|
require.Error(t, err)
|
||||||
|
|
||||||
// Query Delegator Delegations
|
// Query Delegator Delegations
|
||||||
|
|
||||||
|
@ -354,29 +371,29 @@ func TestQueryDelegation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err = queryDelegatorUnbondingDelegations(ctx, query, keeper)
|
res, err = queryDelegatorUnbondingDelegations(ctx, query, keeper)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var delegatorUbds []types.UnbondingDelegation
|
var delegatorUbds []types.UnbondingDelegation
|
||||||
errRes = cdc.UnmarshalJSON(res, &delegatorUbds)
|
errRes = cdc.UnmarshalJSON(res, &delegatorUbds)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
require.Equal(t, unbond, delegatorUbds[0])
|
require.Equal(t, unbond, delegatorUbds[0])
|
||||||
|
|
||||||
// error unknown request
|
// error unknown request
|
||||||
query.Data = bz[:len(bz)-1]
|
query.Data = bz[:len(bz)-1]
|
||||||
|
|
||||||
_, err = queryDelegatorUnbondingDelegations(ctx, query, keeper)
|
_, err = queryDelegatorUnbondingDelegations(ctx, query, keeper)
|
||||||
require.NotNil(t, err)
|
require.Error(t, err)
|
||||||
|
|
||||||
// Query redelegation
|
// Query redelegation
|
||||||
redelegationTokens := sdk.TokensFromConsensusPower(10)
|
redelegationTokens := sdk.TokensFromConsensusPower(10)
|
||||||
_, err = keeper.BeginRedelegation(ctx, addrAcc2, val1.OperatorAddress,
|
_, err = keeper.BeginRedelegation(ctx, addrAcc2, val1.OperatorAddress,
|
||||||
val2.OperatorAddress, redelegationTokens.ToDec())
|
val2.OperatorAddress, redelegationTokens.ToDec())
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
redel, found := keeper.GetRedelegation(ctx, addrAcc2, val1.OperatorAddress, val2.OperatorAddress)
|
redel, found := keeper.GetRedelegation(ctx, addrAcc2, val1.OperatorAddress, val2.OperatorAddress)
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
|
|
||||||
bz, errRes = cdc.MarshalJSON(types.NewQueryRedelegationParams(addrAcc2, val1.OperatorAddress, val2.OperatorAddress))
|
bz, errRes = cdc.MarshalJSON(types.NewQueryRedelegationParams(addrAcc2, val1.OperatorAddress, val2.OperatorAddress))
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
|
|
||||||
query = abci.RequestQuery{
|
query = abci.RequestQuery{
|
||||||
Path: "/custom/staking/redelegations",
|
Path: "/custom/staking/redelegations",
|
||||||
|
@ -384,11 +401,11 @@ func TestQueryDelegation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err = queryRedelegations(ctx, query, keeper)
|
res, err = queryRedelegations(ctx, query, keeper)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var redelRes types.RedelegationResponses
|
var redelRes types.RedelegationResponses
|
||||||
errRes = cdc.UnmarshalJSON(res, &redelRes)
|
errRes = cdc.UnmarshalJSON(res, &redelRes)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
require.Len(t, redelRes, 1)
|
require.Len(t, redelRes, 1)
|
||||||
require.Equal(t, redel.DelegatorAddress, redelRes[0].DelegatorAddress)
|
require.Equal(t, redel.DelegatorAddress, redelRes[0].DelegatorAddress)
|
||||||
require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].ValidatorSrcAddress)
|
require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].ValidatorSrcAddress)
|
||||||
|
@ -420,7 +437,7 @@ func TestQueryRedelegations(t *testing.T) {
|
||||||
// delegator redelegations
|
// delegator redelegations
|
||||||
queryDelegatorParams := types.NewQueryDelegatorParams(addrAcc2)
|
queryDelegatorParams := types.NewQueryDelegatorParams(addrAcc2)
|
||||||
bz, errRes := cdc.MarshalJSON(queryDelegatorParams)
|
bz, errRes := cdc.MarshalJSON(queryDelegatorParams)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
|
|
||||||
query := abci.RequestQuery{
|
query := abci.RequestQuery{
|
||||||
Path: "/custom/staking/redelegations",
|
Path: "/custom/staking/redelegations",
|
||||||
|
@ -428,11 +445,11 @@ func TestQueryRedelegations(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := queryRedelegations(ctx, query, keeper)
|
res, err := queryRedelegations(ctx, query, keeper)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var redelRes types.RedelegationResponses
|
var redelRes types.RedelegationResponses
|
||||||
errRes = cdc.UnmarshalJSON(res, &redelRes)
|
errRes = cdc.UnmarshalJSON(res, &redelRes)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
require.Len(t, redelRes, 1)
|
require.Len(t, redelRes, 1)
|
||||||
require.Equal(t, redel.DelegatorAddress, redelRes[0].DelegatorAddress)
|
require.Equal(t, redel.DelegatorAddress, redelRes[0].DelegatorAddress)
|
||||||
require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].ValidatorSrcAddress)
|
require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].ValidatorSrcAddress)
|
||||||
|
@ -442,7 +459,7 @@ func TestQueryRedelegations(t *testing.T) {
|
||||||
// validator redelegations
|
// validator redelegations
|
||||||
queryValidatorParams := types.NewQueryValidatorParams(val1.GetOperator())
|
queryValidatorParams := types.NewQueryValidatorParams(val1.GetOperator())
|
||||||
bz, errRes = cdc.MarshalJSON(queryValidatorParams)
|
bz, errRes = cdc.MarshalJSON(queryValidatorParams)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
|
|
||||||
query = abci.RequestQuery{
|
query = abci.RequestQuery{
|
||||||
Path: "/custom/staking/redelegations",
|
Path: "/custom/staking/redelegations",
|
||||||
|
@ -450,10 +467,10 @@ func TestQueryRedelegations(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err = queryRedelegations(ctx, query, keeper)
|
res, err = queryRedelegations(ctx, query, keeper)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
errRes = cdc.UnmarshalJSON(res, &redelRes)
|
errRes = cdc.UnmarshalJSON(res, &redelRes)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
require.Len(t, redelRes, 1)
|
require.Len(t, redelRes, 1)
|
||||||
require.Equal(t, redel.DelegatorAddress, redelRes[0].DelegatorAddress)
|
require.Equal(t, redel.DelegatorAddress, redelRes[0].DelegatorAddress)
|
||||||
require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].ValidatorSrcAddress)
|
require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].ValidatorSrcAddress)
|
||||||
|
@ -489,13 +506,13 @@ func TestQueryUnbondingDelegation(t *testing.T) {
|
||||||
//
|
//
|
||||||
queryValidatorParams := types.NewQueryBondsParams(addrAcc1, val1.GetOperator())
|
queryValidatorParams := types.NewQueryBondsParams(addrAcc1, val1.GetOperator())
|
||||||
bz, errRes := cdc.MarshalJSON(queryValidatorParams)
|
bz, errRes := cdc.MarshalJSON(queryValidatorParams)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
query := abci.RequestQuery{
|
query := abci.RequestQuery{
|
||||||
Path: "/custom/staking/unbondingDelegation",
|
Path: "/custom/staking/unbondingDelegation",
|
||||||
Data: bz,
|
Data: bz,
|
||||||
}
|
}
|
||||||
res, err := queryUnbondingDelegation(ctx, query, keeper)
|
res, err := queryUnbondingDelegation(ctx, query, keeper)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, res)
|
require.NotNil(t, res)
|
||||||
var ubDel types.UnbondingDelegation
|
var ubDel types.UnbondingDelegation
|
||||||
require.NoError(t, cdc.UnmarshalJSON(res, &ubDel))
|
require.NoError(t, cdc.UnmarshalJSON(res, &ubDel))
|
||||||
|
@ -508,26 +525,26 @@ func TestQueryUnbondingDelegation(t *testing.T) {
|
||||||
//
|
//
|
||||||
queryValidatorParams = types.NewQueryBondsParams(addrAcc2, val1.GetOperator())
|
queryValidatorParams = types.NewQueryBondsParams(addrAcc2, val1.GetOperator())
|
||||||
bz, errRes = cdc.MarshalJSON(queryValidatorParams)
|
bz, errRes = cdc.MarshalJSON(queryValidatorParams)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
query = abci.RequestQuery{
|
query = abci.RequestQuery{
|
||||||
Path: "/custom/staking/unbondingDelegation",
|
Path: "/custom/staking/unbondingDelegation",
|
||||||
Data: bz,
|
Data: bz,
|
||||||
}
|
}
|
||||||
_, err = queryUnbondingDelegation(ctx, query, keeper)
|
_, err = queryUnbondingDelegation(ctx, query, keeper)
|
||||||
require.NotNil(t, err)
|
require.Error(t, err)
|
||||||
|
|
||||||
//
|
//
|
||||||
// found: query unbonding delegation by delegator and validator
|
// found: query unbonding delegation by delegator and validator
|
||||||
//
|
//
|
||||||
queryDelegatorParams := types.NewQueryDelegatorParams(addrAcc1)
|
queryDelegatorParams := types.NewQueryDelegatorParams(addrAcc1)
|
||||||
bz, errRes = cdc.MarshalJSON(queryDelegatorParams)
|
bz, errRes = cdc.MarshalJSON(queryDelegatorParams)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
query = abci.RequestQuery{
|
query = abci.RequestQuery{
|
||||||
Path: "/custom/staking/delegatorUnbondingDelegations",
|
Path: "/custom/staking/delegatorUnbondingDelegations",
|
||||||
Data: bz,
|
Data: bz,
|
||||||
}
|
}
|
||||||
res, err = queryDelegatorUnbondingDelegations(ctx, query, keeper)
|
res, err = queryDelegatorUnbondingDelegations(ctx, query, keeper)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, res)
|
require.NotNil(t, res)
|
||||||
var ubDels []types.UnbondingDelegation
|
var ubDels []types.UnbondingDelegation
|
||||||
require.NoError(t, cdc.UnmarshalJSON(res, &ubDels))
|
require.NoError(t, cdc.UnmarshalJSON(res, &ubDels))
|
||||||
|
@ -540,14 +557,56 @@ func TestQueryUnbondingDelegation(t *testing.T) {
|
||||||
//
|
//
|
||||||
queryDelegatorParams = types.NewQueryDelegatorParams(addrAcc2)
|
queryDelegatorParams = types.NewQueryDelegatorParams(addrAcc2)
|
||||||
bz, errRes = cdc.MarshalJSON(queryDelegatorParams)
|
bz, errRes = cdc.MarshalJSON(queryDelegatorParams)
|
||||||
require.Nil(t, errRes)
|
require.NoError(t, errRes)
|
||||||
query = abci.RequestQuery{
|
query = abci.RequestQuery{
|
||||||
Path: "/custom/staking/delegatorUnbondingDelegations",
|
Path: "/custom/staking/delegatorUnbondingDelegations",
|
||||||
Data: bz,
|
Data: bz,
|
||||||
}
|
}
|
||||||
res, err = queryDelegatorUnbondingDelegations(ctx, query, keeper)
|
res, err = queryDelegatorUnbondingDelegations(ctx, query, keeper)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, res)
|
require.NotNil(t, res)
|
||||||
require.NoError(t, cdc.UnmarshalJSON(res, &ubDels))
|
require.NoError(t, cdc.UnmarshalJSON(res, &ubDels))
|
||||||
require.Equal(t, 0, len(ubDels))
|
require.Equal(t, 0, len(ubDels))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestQueryHistoricalInfo(t *testing.T) {
|
||||||
|
cdc := codec.New()
|
||||||
|
ctx, _, keeper, _ := CreateTestInput(t, false, 10000)
|
||||||
|
|
||||||
|
// Create Validators and Delegation
|
||||||
|
val1 := types.NewValidator(addrVal1, pk1, types.Description{})
|
||||||
|
val2 := types.NewValidator(addrVal2, pk2, types.Description{})
|
||||||
|
vals := []types.Validator{val1, val2}
|
||||||
|
keeper.SetValidator(ctx, val1)
|
||||||
|
keeper.SetValidator(ctx, val2)
|
||||||
|
|
||||||
|
header := abci.Header{
|
||||||
|
ChainID: "HelloChain",
|
||||||
|
Height: 5,
|
||||||
|
}
|
||||||
|
hi := types.NewHistoricalInfo(header, vals)
|
||||||
|
keeper.SetHistoricalInfo(ctx, 5, hi)
|
||||||
|
|
||||||
|
queryHistoricalParams := types.NewQueryHistoricalInfoParams(4)
|
||||||
|
bz, errRes := cdc.MarshalJSON(queryHistoricalParams)
|
||||||
|
require.NoError(t, errRes)
|
||||||
|
query := abci.RequestQuery{
|
||||||
|
Path: "/custom/staking/historicalInfo",
|
||||||
|
Data: bz,
|
||||||
|
}
|
||||||
|
res, err := queryHistoricalInfo(ctx, query, keeper)
|
||||||
|
require.Error(t, err, "Invalid query passed")
|
||||||
|
require.Nil(t, res, "Invalid query returned non-nil result")
|
||||||
|
|
||||||
|
queryHistoricalParams = types.NewQueryHistoricalInfoParams(5)
|
||||||
|
bz, errRes = cdc.MarshalJSON(queryHistoricalParams)
|
||||||
|
require.NoError(t, errRes)
|
||||||
|
query.Data = bz
|
||||||
|
res, err = queryHistoricalInfo(ctx, query, keeper)
|
||||||
|
require.NoError(t, err, "Valid query passed")
|
||||||
|
require.NotNil(t, res, "Valid query returned nil result")
|
||||||
|
|
||||||
|
var recv types.HistoricalInfo
|
||||||
|
require.NoError(t, cdc.UnmarshalJSON(res, &recv))
|
||||||
|
require.Equal(t, hi, recv, "HistoricalInfo query returned wrong result")
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,62 @@ import (
|
||||||
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Calculate the ValidatorUpdates for the current block
|
||||||
|
// Called in each EndBlock
|
||||||
|
func (k Keeper) BlockValidatorUpdates(ctx sdk.Context) []abci.ValidatorUpdate {
|
||||||
|
// Calculate validator set changes.
|
||||||
|
//
|
||||||
|
// NOTE: ApplyAndReturnValidatorSetUpdates has to come before
|
||||||
|
// UnbondAllMatureValidatorQueue.
|
||||||
|
// This fixes a bug when the unbonding period is instant (is the case in
|
||||||
|
// some of the tests). The test expected the validator to be completely
|
||||||
|
// unbonded after the Endblocker (go from Bonded -> Unbonding during
|
||||||
|
// ApplyAndReturnValidatorSetUpdates and then Unbonding -> Unbonded during
|
||||||
|
// UnbondAllMatureValidatorQueue).
|
||||||
|
validatorUpdates := k.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||||
|
|
||||||
|
// Unbond all mature validators from the unbonding queue.
|
||||||
|
k.UnbondAllMatureValidatorQueue(ctx)
|
||||||
|
|
||||||
|
// Remove all mature unbonding delegations from the ubd queue.
|
||||||
|
matureUnbonds := k.DequeueAllMatureUBDQueue(ctx, ctx.BlockHeader().Time)
|
||||||
|
for _, dvPair := range matureUnbonds {
|
||||||
|
err := k.CompleteUnbonding(ctx, dvPair.DelegatorAddress, dvPair.ValidatorAddress)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.EventManager().EmitEvent(
|
||||||
|
sdk.NewEvent(
|
||||||
|
types.EventTypeCompleteUnbonding,
|
||||||
|
sdk.NewAttribute(types.AttributeKeyValidator, dvPair.ValidatorAddress.String()),
|
||||||
|
sdk.NewAttribute(types.AttributeKeyDelegator, dvPair.DelegatorAddress.String()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all mature redelegations from the red queue.
|
||||||
|
matureRedelegations := k.DequeueAllMatureRedelegationQueue(ctx, ctx.BlockHeader().Time)
|
||||||
|
for _, dvvTriplet := range matureRedelegations {
|
||||||
|
err := k.CompleteRedelegation(ctx, dvvTriplet.DelegatorAddress,
|
||||||
|
dvvTriplet.ValidatorSrcAddress, dvvTriplet.ValidatorDstAddress)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.EventManager().EmitEvent(
|
||||||
|
sdk.NewEvent(
|
||||||
|
types.EventTypeCompleteRedelegation,
|
||||||
|
sdk.NewAttribute(types.AttributeKeyDelegator, dvvTriplet.DelegatorAddress.String()),
|
||||||
|
sdk.NewAttribute(types.AttributeKeySrcValidator, dvvTriplet.ValidatorSrcAddress.String()),
|
||||||
|
sdk.NewAttribute(types.AttributeKeyDstValidator, dvvTriplet.ValidatorDstAddress.String()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return validatorUpdates
|
||||||
|
}
|
||||||
|
|
||||||
// Apply and return accumulated updates to the bonded validator set. Also,
|
// Apply and return accumulated updates to the bonded validator set. Also,
|
||||||
// * Updates the active valset as keyed by LastValidatorPowerKey.
|
// * Updates the active valset as keyed by LastValidatorPowerKey.
|
||||||
// * Updates the total power as keyed by LastTotalPowerKey.
|
// * Updates the total power as keyed by LastTotalPowerKey.
|
||||||
|
|
|
@ -165,7 +165,9 @@ func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
// BeginBlock returns the begin blocker for the staking module.
|
// BeginBlock returns the begin blocker for the staking module.
|
||||||
func (AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {}
|
func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) {
|
||||||
|
BeginBlocker(ctx, am.keeper)
|
||||||
|
}
|
||||||
|
|
||||||
// EndBlock returns the end blocker for the staking module. It returns no validator
|
// EndBlock returns the end blocker for the staking module. It returns no validator
|
||||||
// updates.
|
// updates.
|
||||||
|
|
|
@ -50,7 +50,7 @@ func RandomizedGenState(simState *module.SimulationState) {
|
||||||
// NewSimulationManager constructor for this to work
|
// NewSimulationManager constructor for this to work
|
||||||
simState.UnbondTime = unbondTime
|
simState.UnbondTime = unbondTime
|
||||||
|
|
||||||
params := types.NewParams(simState.UnbondTime, maxValidators, 7, sdk.DefaultBondDenom)
|
params := types.NewParams(simState.UnbondTime, maxValidators, 7, 3, sdk.DefaultBondDenom)
|
||||||
|
|
||||||
// validators & delegations
|
// validators & delegations
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -282,3 +282,21 @@ The stored object as each key is an array of validator operator addresses from
|
||||||
which the validator object can be accessed. Typically it is expected that only
|
which the validator object can be accessed. Typically it is expected that only
|
||||||
a single validator record will be associated with a given timestamp however it is possible
|
a single validator record will be associated with a given timestamp however it is possible
|
||||||
that multiple validators exist in the queue at the same location.
|
that multiple validators exist in the queue at the same location.
|
||||||
|
|
||||||
|
## HistoricalInfo
|
||||||
|
|
||||||
|
HistoricalInfo objects are stored and pruned at each block such that the staking keeper persists
|
||||||
|
the `n` most recent historical info defined by staking module parameter: `HistoricalEntries`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type HistoricalInfo struct {
|
||||||
|
Header abci.Header
|
||||||
|
ValSet []types.Validator
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
At each BeginBlock, the staking keeper will persist the current Header and the Validators that committed
|
||||||
|
the current block in a `HistoricalInfo` object. The Validators are sorted on their address to ensure that
|
||||||
|
they are in a determisnistic order.
|
||||||
|
The oldest HistoricalEntries will be pruned to ensure that there only exist the parameter-defined number of
|
||||||
|
historical entries.
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
<!--
|
||||||
|
order: 4
|
||||||
|
-->
|
||||||
|
|
||||||
|
# Begin-Block
|
||||||
|
|
||||||
|
Each abci begin block call, the historical info will get stored and pruned
|
||||||
|
according to the `HistoricalEntries` parameter.
|
||||||
|
|
||||||
|
## Historical Info Tracking
|
||||||
|
|
||||||
|
If the `HistoricalEntries` parameter is 0, then the `BeginBlock` performs a no-op.
|
||||||
|
|
||||||
|
Otherwise, the latest historical info is stored under the key `historicalInfoKey|height`, while any entries older than `height - HistoricalEntries` is deleted.
|
||||||
|
In most cases, this results in a single entry being pruned per block.
|
||||||
|
However, if the parameter `HistoricalEntries` has changed to a lower value there will be multiple entries in the store that must be pruned.
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
order: 4
|
order: 5
|
||||||
-->
|
-->
|
||||||
|
|
||||||
# End-Block
|
# End-Block
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
order: 5
|
order: 6
|
||||||
-->
|
-->
|
||||||
|
|
||||||
# Hooks
|
# Hooks
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
order: 6
|
order: 7
|
||||||
-->
|
-->
|
||||||
|
|
||||||
# Events
|
# Events
|
|
@ -1,14 +0,0 @@
|
||||||
<!--
|
|
||||||
order: 7
|
|
||||||
-->
|
|
||||||
|
|
||||||
# Parameters
|
|
||||||
|
|
||||||
The staking module contains the following parameters:
|
|
||||||
|
|
||||||
| Key | Type | Example |
|
|
||||||
|---------------|------------------|-------------------|
|
|
||||||
| UnbondingTime | string (time ns) | "259200000000000" |
|
|
||||||
| MaxValidators | uint16 | 100 |
|
|
||||||
| KeyMaxEntries | uint16 | 7 |
|
|
||||||
| BondDenom | string | "uatom" |
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!--
|
||||||
|
order: 8
|
||||||
|
-->
|
||||||
|
|
||||||
|
# Parameters
|
||||||
|
|
||||||
|
The staking module contains the following parameters:
|
||||||
|
|
||||||
|
| Key | Type | Example |
|
||||||
|
|-------------------|------------------|-------------------|
|
||||||
|
| UnbondingTime | string (time ns) | "259200000000000" |
|
||||||
|
| MaxValidators | uint16 | 100 |
|
||||||
|
| KeyMaxEntries | uint16 | 7 |
|
||||||
|
| HistoricalEntries | uint16 | 3 |
|
||||||
|
| BondDenom | string | "uatom" |
|
|
@ -32,6 +32,7 @@ network.
|
||||||
- [UnbondingDelegation](01_state.md#unbondingdelegation)
|
- [UnbondingDelegation](01_state.md#unbondingdelegation)
|
||||||
- [Redelegation](01_state.md#redelegation)
|
- [Redelegation](01_state.md#redelegation)
|
||||||
- [Queues](01_state.md#queues)
|
- [Queues](01_state.md#queues)
|
||||||
|
- [HistoricalInfo](01_state.md#historicalinfo)
|
||||||
2. **[State Transitions](02_state_transitions.md)**
|
2. **[State Transitions](02_state_transitions.md)**
|
||||||
- [Validators](02_state_transitions.md#validators)
|
- [Validators](02_state_transitions.md#validators)
|
||||||
- [Delegations](02_state_transitions.md#delegations)
|
- [Delegations](02_state_transitions.md#delegations)
|
||||||
|
@ -42,11 +43,13 @@ network.
|
||||||
- [MsgDelegate](03_messages.md#msgdelegate)
|
- [MsgDelegate](03_messages.md#msgdelegate)
|
||||||
- [MsgBeginUnbonding](03_messages.md#msgbeginunbonding)
|
- [MsgBeginUnbonding](03_messages.md#msgbeginunbonding)
|
||||||
- [MsgBeginRedelegate](03_messages.md#msgbeginredelegate)
|
- [MsgBeginRedelegate](03_messages.md#msgbeginredelegate)
|
||||||
4. **[End-Block ](04_end_block.md)**
|
4. **[Begin-Block](04_begin_block.md)**
|
||||||
- [Validator Set Changes](04_end_block.md#validator-set-changes)
|
- [Historical Info Tracking](04_begin_block.md#historical-info-tracking)
|
||||||
- [Queues ](04_end_block.md#queues-)
|
4. **[End-Block ](05_end_block.md)**
|
||||||
5. **[Hooks](05_hooks.md)**
|
- [Validator Set Changes](05_end_block.md#validator-set-changes)
|
||||||
6. **[Events](06_events.md)**
|
- [Queues ](05_end_block.md#queues-)
|
||||||
- [EndBlocker](06_events.md#endblocker)
|
5. **[Hooks](06_hooks.md)**
|
||||||
- [Handlers](06_events.md#handlers)
|
6. **[Events](07_events.md)**
|
||||||
7. **[Parameters](07_params.md)**
|
- [EndBlocker](07_events.md#endblocker)
|
||||||
|
- [Handlers](07_events.md#handlers)
|
||||||
|
7. **[Parameters](08_params.md)**
|
||||||
|
|
|
@ -14,14 +14,15 @@ type CodeType = sdk.CodeType
|
||||||
const (
|
const (
|
||||||
DefaultCodespace sdk.CodespaceType = ModuleName
|
DefaultCodespace sdk.CodespaceType = ModuleName
|
||||||
|
|
||||||
CodeInvalidValidator CodeType = 101
|
CodeInvalidValidator CodeType = 101
|
||||||
CodeInvalidDelegation CodeType = 102
|
CodeInvalidDelegation CodeType = 102
|
||||||
CodeInvalidInput CodeType = 103
|
CodeInvalidInput CodeType = 103
|
||||||
CodeValidatorJailed CodeType = 104
|
CodeValidatorJailed CodeType = 104
|
||||||
CodeInvalidAddress CodeType = sdk.CodeInvalidAddress
|
CodeInvalidHistoricalInfo CodeType = 105
|
||||||
CodeUnauthorized CodeType = sdk.CodeUnauthorized
|
CodeInvalidAddress CodeType = sdk.CodeInvalidAddress
|
||||||
CodeInternal CodeType = sdk.CodeInternal
|
CodeUnauthorized CodeType = sdk.CodeUnauthorized
|
||||||
CodeUnknownRequest CodeType = sdk.CodeUnknownRequest
|
CodeInternal CodeType = sdk.CodeInternal
|
||||||
|
CodeUnknownRequest CodeType = sdk.CodeUnknownRequest
|
||||||
)
|
)
|
||||||
|
|
||||||
//validator
|
//validator
|
||||||
|
@ -212,3 +213,11 @@ func ErrNeitherShareMsgsGiven(codespace sdk.CodespaceType) sdk.Error {
|
||||||
func ErrMissingSignature(codespace sdk.CodespaceType) sdk.Error {
|
func ErrMissingSignature(codespace sdk.CodespaceType) sdk.Error {
|
||||||
return sdk.NewError(codespace, CodeInvalidValidator, "missing signature")
|
return sdk.NewError(codespace, CodeInvalidValidator, "missing signature")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ErrInvalidHistoricalInfo(codespace sdk.CodespaceType) sdk.Error {
|
||||||
|
return sdk.NewError(codespace, CodeInvalidHistoricalInfo, "invalid historical info")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrNoHistoricalInfo(codespace sdk.CodespaceType) sdk.Error {
|
||||||
|
return sdk.NewError(codespace, CodeInvalidHistoricalInfo, "no historical info found")
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HistoricalInfo contains the historical information that gets stored at each height
|
||||||
|
type HistoricalInfo struct {
|
||||||
|
Header abci.Header `json:"header" yaml:"header"`
|
||||||
|
ValSet []Validator `json:"valset" yaml:"valset"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHistoricalInfo will create a historical information struct from header and valset
|
||||||
|
// it will first sort valset before inclusion into historical info
|
||||||
|
func NewHistoricalInfo(header abci.Header, valSet []Validator) HistoricalInfo {
|
||||||
|
sort.Sort(Validators(valSet))
|
||||||
|
return HistoricalInfo{
|
||||||
|
Header: header,
|
||||||
|
ValSet: valSet,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustMarshalHistoricalInfo wll marshal historical info and panic on error
|
||||||
|
func MustMarshalHistoricalInfo(cdc *codec.Codec, hi HistoricalInfo) []byte {
|
||||||
|
return cdc.MustMarshalBinaryLengthPrefixed(hi)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustUnmarshalHistoricalInfo wll unmarshal historical info and panic on error
|
||||||
|
func MustUnmarshalHistoricalInfo(cdc *codec.Codec, value []byte) HistoricalInfo {
|
||||||
|
hi, err := UnmarshalHistoricalInfo(cdc, value)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return hi
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalHistoricalInfo will unmarshal historical info and return any error
|
||||||
|
func UnmarshalHistoricalInfo(cdc *codec.Codec, value []byte) (hi HistoricalInfo, err error) {
|
||||||
|
err = cdc.UnmarshalBinaryLengthPrefixed(value, &hi)
|
||||||
|
return hi, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateBasic will ensure HistoricalInfo is not nil and sorted
|
||||||
|
func ValidateBasic(hi HistoricalInfo) error {
|
||||||
|
if len(hi.ValSet) == 0 {
|
||||||
|
return sdkerrors.Wrap(ErrInvalidHistoricalInfo(DefaultCodespace), "ValidatorSer is nil")
|
||||||
|
}
|
||||||
|
if !sort.IsSorted(Validators(hi.ValSet)) {
|
||||||
|
return sdkerrors.Wrap(ErrInvalidHistoricalInfo(DefaultCodespace), "ValidatorSet is not sorted by address")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
validators = []Validator{
|
||||||
|
NewValidator(valAddr1, pk1, Description{}),
|
||||||
|
NewValidator(valAddr2, pk2, Description{}),
|
||||||
|
NewValidator(valAddr3, pk3, Description{}),
|
||||||
|
}
|
||||||
|
header = abci.Header{
|
||||||
|
ChainID: "hello",
|
||||||
|
Height: 5,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHistoricalInfo(t *testing.T) {
|
||||||
|
hi := NewHistoricalInfo(header, validators)
|
||||||
|
require.True(t, sort.IsSorted(Validators(hi.ValSet)), "Validators are not sorted")
|
||||||
|
|
||||||
|
var value []byte
|
||||||
|
require.NotPanics(t, func() {
|
||||||
|
value = MustMarshalHistoricalInfo(ModuleCdc, hi)
|
||||||
|
})
|
||||||
|
|
||||||
|
require.NotNil(t, value, "Marshalled HistoricalInfo is nil")
|
||||||
|
|
||||||
|
recv, err := UnmarshalHistoricalInfo(ModuleCdc, value)
|
||||||
|
require.Nil(t, err, "Unmarshalling HistoricalInfo failed")
|
||||||
|
require.Equal(t, hi, recv, "Unmarshalled HistoricalInfo is different from original")
|
||||||
|
require.True(t, sort.IsSorted(Validators(hi.ValSet)), "Validators are not sorted")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateBasic(t *testing.T) {
|
||||||
|
hi := HistoricalInfo{
|
||||||
|
Header: header,
|
||||||
|
}
|
||||||
|
err := ValidateBasic(hi)
|
||||||
|
require.Error(t, err, "ValidateBasic passed on nil ValSet")
|
||||||
|
|
||||||
|
// Ensure validators are not sorted
|
||||||
|
for sort.IsSorted(Validators(validators)) {
|
||||||
|
rand.Shuffle(len(validators), func(i, j int) {
|
||||||
|
it := validators[i]
|
||||||
|
validators[i] = validators[j]
|
||||||
|
validators[j] = it
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
hi = HistoricalInfo{
|
||||||
|
Header: header,
|
||||||
|
ValSet: validators,
|
||||||
|
}
|
||||||
|
err = ValidateBasic(hi)
|
||||||
|
require.Error(t, err, "ValidateBasic passed on unsorted ValSet")
|
||||||
|
|
||||||
|
hi = NewHistoricalInfo(header, validators)
|
||||||
|
err = ValidateBasic(hi)
|
||||||
|
require.NoError(t, err, "ValidateBasic failed on valid HistoricalInfo")
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
@ -45,6 +46,8 @@ var (
|
||||||
UnbondingQueueKey = []byte{0x41} // prefix for the timestamps in unbonding queue
|
UnbondingQueueKey = []byte{0x41} // prefix for the timestamps in unbonding queue
|
||||||
RedelegationQueueKey = []byte{0x42} // prefix for the timestamps in redelegations queue
|
RedelegationQueueKey = []byte{0x42} // prefix for the timestamps in redelegations queue
|
||||||
ValidatorQueueKey = []byte{0x43} // prefix for the timestamps in validator queue
|
ValidatorQueueKey = []byte{0x43} // prefix for the timestamps in validator queue
|
||||||
|
|
||||||
|
HistoricalInfoKey = []byte{0x50} // prefix for the historical info
|
||||||
)
|
)
|
||||||
|
|
||||||
// gets the key for the validator with address
|
// gets the key for the validator with address
|
||||||
|
@ -278,3 +281,10 @@ func GetREDsByDelToValDstIndexKey(delAddr sdk.AccAddress, valDstAddr sdk.ValAddr
|
||||||
GetREDsToValDstIndexKey(valDstAddr),
|
GetREDsToValDstIndexKey(valDstAddr),
|
||||||
delAddr.Bytes()...)
|
delAddr.Bytes()...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//________________________________________________________________________________
|
||||||
|
|
||||||
|
// GetHistoricalInfoKey gets the key for the historical info
|
||||||
|
func GetHistoricalInfoKey(height int64) []byte {
|
||||||
|
return append(HistoricalInfoKey, []byte(strconv.FormatInt(height, 10))...)
|
||||||
|
}
|
||||||
|
|
|
@ -24,35 +24,42 @@ const (
|
||||||
|
|
||||||
// Default maximum entries in a UBD/RED pair
|
// Default maximum entries in a UBD/RED pair
|
||||||
DefaultMaxEntries uint16 = 7
|
DefaultMaxEntries uint16 = 7
|
||||||
|
|
||||||
|
// DefaultHistorical entries is 0 since it must only be non-zero for
|
||||||
|
// IBC connected chains
|
||||||
|
DefaultHistoricalEntries uint16 = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
// nolint - Keys for parameter access
|
// nolint - Keys for parameter access
|
||||||
var (
|
var (
|
||||||
KeyUnbondingTime = []byte("UnbondingTime")
|
KeyUnbondingTime = []byte("UnbondingTime")
|
||||||
KeyMaxValidators = []byte("MaxValidators")
|
KeyMaxValidators = []byte("MaxValidators")
|
||||||
KeyMaxEntries = []byte("KeyMaxEntries")
|
KeyMaxEntries = []byte("KeyMaxEntries")
|
||||||
KeyBondDenom = []byte("BondDenom")
|
KeyBondDenom = []byte("BondDenom")
|
||||||
|
KeyHistoricalEntries = []byte("HistoricalEntries")
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ params.ParamSet = (*Params)(nil)
|
var _ params.ParamSet = (*Params)(nil)
|
||||||
|
|
||||||
// Params defines the high level settings for staking
|
// Params defines the high level settings for staking
|
||||||
type Params struct {
|
type Params struct {
|
||||||
UnbondingTime time.Duration `json:"unbonding_time" yaml:"unbonding_time"` // time duration of unbonding
|
UnbondingTime time.Duration `json:"unbonding_time" yaml:"unbonding_time"` // time duration of unbonding
|
||||||
MaxValidators uint16 `json:"max_validators" yaml:"max_validators"` // maximum number of validators (max uint16 = 65535)
|
MaxValidators uint16 `json:"max_validators" yaml:"max_validators"` // maximum number of validators (max uint16 = 65535)
|
||||||
MaxEntries uint16 `json:"max_entries" yaml:"max_entries"` // max entries for either unbonding delegation or redelegation (per pair/trio)
|
MaxEntries uint16 `json:"max_entries" yaml:"max_entries"` // max entries for either unbonding delegation or redelegation (per pair/trio)
|
||||||
BondDenom string `json:"bond_denom" yaml:"bond_denom"` // bondable coin denomination
|
HistoricalEntries uint16 `json:"historical_entries" yaml:"historical_entries"` // number of historical entries to persist
|
||||||
|
BondDenom string `json:"bond_denom" yaml:"bond_denom"` // bondable coin denomination
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewParams creates a new Params instance
|
// NewParams creates a new Params instance
|
||||||
func NewParams(unbondingTime time.Duration, maxValidators, maxEntries uint16,
|
func NewParams(unbondingTime time.Duration, maxValidators, maxEntries, historicalEntries uint16,
|
||||||
bondDenom string) Params {
|
bondDenom string) Params {
|
||||||
|
|
||||||
return Params{
|
return Params{
|
||||||
UnbondingTime: unbondingTime,
|
UnbondingTime: unbondingTime,
|
||||||
MaxValidators: maxValidators,
|
MaxValidators: maxValidators,
|
||||||
MaxEntries: maxEntries,
|
MaxEntries: maxEntries,
|
||||||
BondDenom: bondDenom,
|
HistoricalEntries: historicalEntries,
|
||||||
|
BondDenom: bondDenom,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +69,7 @@ func (p *Params) ParamSetPairs() params.ParamSetPairs {
|
||||||
params.NewParamSetPair(KeyUnbondingTime, &p.UnbondingTime, validateUnbondingTime),
|
params.NewParamSetPair(KeyUnbondingTime, &p.UnbondingTime, validateUnbondingTime),
|
||||||
params.NewParamSetPair(KeyMaxValidators, &p.MaxValidators, validateMaxValidators),
|
params.NewParamSetPair(KeyMaxValidators, &p.MaxValidators, validateMaxValidators),
|
||||||
params.NewParamSetPair(KeyMaxEntries, &p.MaxEntries, validateMaxEntries),
|
params.NewParamSetPair(KeyMaxEntries, &p.MaxEntries, validateMaxEntries),
|
||||||
|
params.NewParamSetPair(KeyHistoricalEntries, &p.HistoricalEntries, validateHistoricalEntries),
|
||||||
params.NewParamSetPair(KeyBondDenom, &p.BondDenom, validateBondDenom),
|
params.NewParamSetPair(KeyBondDenom, &p.BondDenom, validateBondDenom),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,17 +84,18 @@ func (p Params) Equal(p2 Params) bool {
|
||||||
|
|
||||||
// DefaultParams returns a default set of parameters.
|
// DefaultParams returns a default set of parameters.
|
||||||
func DefaultParams() Params {
|
func DefaultParams() Params {
|
||||||
return NewParams(DefaultUnbondingTime, DefaultMaxValidators, DefaultMaxEntries, sdk.DefaultBondDenom)
|
return NewParams(DefaultUnbondingTime, DefaultMaxValidators, DefaultMaxEntries, DefaultHistoricalEntries, sdk.DefaultBondDenom)
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns a human readable string representation of the parameters.
|
// String returns a human readable string representation of the parameters.
|
||||||
func (p Params) String() string {
|
func (p Params) String() string {
|
||||||
return fmt.Sprintf(`Params:
|
return fmt.Sprintf(`Params:
|
||||||
Unbonding Time: %s
|
Unbonding Time: %s
|
||||||
Max Validators: %d
|
Max Validators: %d
|
||||||
Max Entries: %d
|
Max Entries: %d
|
||||||
Bonded Coin Denom: %s`, p.UnbondingTime,
|
Historical Entries: %d
|
||||||
p.MaxValidators, p.MaxEntries, p.BondDenom)
|
Bonded Coin Denom: %s`, p.UnbondingTime,
|
||||||
|
p.MaxValidators, p.MaxEntries, p.HistoricalEntries, p.BondDenom)
|
||||||
}
|
}
|
||||||
|
|
||||||
// unmarshal the current staking params value from store key or panic
|
// unmarshal the current staking params value from store key or panic
|
||||||
|
@ -164,6 +173,15 @@ func validateMaxEntries(i interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateHistoricalEntries(i interface{}) error {
|
||||||
|
_, ok := i.(uint16)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid parameter type: %T", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func validateBondDenom(i interface{}) error {
|
func validateBondDenom(i interface{}) error {
|
||||||
v, ok := i.(string)
|
v, ok := i.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
@ -20,6 +20,7 @@ const (
|
||||||
QueryDelegatorValidator = "delegatorValidator"
|
QueryDelegatorValidator = "delegatorValidator"
|
||||||
QueryPool = "pool"
|
QueryPool = "pool"
|
||||||
QueryParameters = "parameters"
|
QueryParameters = "parameters"
|
||||||
|
QueryHistoricalInfo = "historicalInfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
// defines the params for the following queries:
|
// defines the params for the following queries:
|
||||||
|
@ -96,3 +97,14 @@ type QueryValidatorsParams struct {
|
||||||
func NewQueryValidatorsParams(page, limit int, status string) QueryValidatorsParams {
|
func NewQueryValidatorsParams(page, limit int, status string) QueryValidatorsParams {
|
||||||
return QueryValidatorsParams{page, limit, status}
|
return QueryValidatorsParams{page, limit, status}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QueryHistoricalInfoParams defines the params for the following queries:
|
||||||
|
// - 'custom/staking/historicalInfo'
|
||||||
|
type QueryHistoricalInfoParams struct {
|
||||||
|
Height int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewQueryHistoricalInfoParams creates a new QueryHistoricalInfoParams instance
|
||||||
|
func NewQueryHistoricalInfoParams(height int64) QueryHistoricalInfoParams {
|
||||||
|
return QueryHistoricalInfoParams{height}
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package types
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -102,6 +103,28 @@ func (v Validators) ToSDKValidators() (validators []exported.ValidatorI) {
|
||||||
return validators
|
return validators
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort Validators sorts validator array in ascending operator address order
|
||||||
|
func (v Validators) Sort() {
|
||||||
|
sort.Sort(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements sort interface
|
||||||
|
func (v Validators) Len() int {
|
||||||
|
return len(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements sort interface
|
||||||
|
func (v Validators) Less(i, j int) bool {
|
||||||
|
return bytes.Compare(v[i].OperatorAddress, v[j].OperatorAddress) == -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements sort interface
|
||||||
|
func (v Validators) Swap(i, j int) {
|
||||||
|
it := v[i]
|
||||||
|
v[i] = v[j]
|
||||||
|
v[j] = it
|
||||||
|
}
|
||||||
|
|
||||||
// NewValidator - initialize a new validator
|
// NewValidator - initialize a new validator
|
||||||
func NewValidator(operator sdk.ValAddress, pubKey crypto.PubKey, description Description) Validator {
|
func NewValidator(operator sdk.ValAddress, pubKey crypto.PubKey, description Description) Validator {
|
||||||
return Validator{
|
return Validator{
|
||||||
|
|
|
@ -2,10 +2,14 @@ package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
yaml "gopkg.in/yaml.v2"
|
yaml "gopkg.in/yaml.v2"
|
||||||
|
|
||||||
|
@ -303,3 +307,31 @@ func TestValidatorMarshalYAML(t *testing.T) {
|
||||||
`, validator.OperatorAddress.String(), bechifiedPub)
|
`, validator.OperatorAddress.String(), bechifiedPub)
|
||||||
require.Equal(t, want, string(bs))
|
require.Equal(t, want, string(bs))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that sort will create deterministic ordering of validators
|
||||||
|
func TestValidatorsSortDeterminism(t *testing.T) {
|
||||||
|
vals := make([]Validator, 10)
|
||||||
|
sortedVals := make([]Validator, 10)
|
||||||
|
|
||||||
|
// Create random validator slice
|
||||||
|
for i := range vals {
|
||||||
|
pk := ed25519.GenPrivKey().PubKey()
|
||||||
|
vals[i] = NewValidator(sdk.ValAddress(pk.Address()), pk, Description{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save sorted copy
|
||||||
|
sort.Sort(Validators(vals))
|
||||||
|
copy(sortedVals, vals)
|
||||||
|
|
||||||
|
// Randomly shuffle validators, sort, and check it is equal to original sort
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
rand.Shuffle(10, func(i, j int) {
|
||||||
|
it := vals[i]
|
||||||
|
vals[i] = vals[j]
|
||||||
|
vals[j] = it
|
||||||
|
})
|
||||||
|
|
||||||
|
Validators(vals).Sort()
|
||||||
|
require.True(t, reflect.DeepEqual(sortedVals, vals), "Validator sort returned different slices")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue