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.
|
||||
* `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.
|
||||
* (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).
|
||||
* (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
|
||||
CodeInvalidInput = types.CodeInvalidInput
|
||||
CodeValidatorJailed = types.CodeValidatorJailed
|
||||
CodeInvalidHistoricalInfo = types.CodeInvalidHistoricalInfo
|
||||
CodeInvalidAddress = types.CodeInvalidAddress
|
||||
CodeUnauthorized = types.CodeUnauthorized
|
||||
CodeInternal = types.CodeInternal
|
||||
|
@ -47,6 +48,7 @@ const (
|
|||
QueryDelegatorValidator = types.QueryDelegatorValidator
|
||||
QueryPool = types.QueryPool
|
||||
QueryParameters = types.QueryParameters
|
||||
QueryHistoricalInfo = types.QueryHistoricalInfo
|
||||
MaxMonikerLength = types.MaxMonikerLength
|
||||
MaxIdentityLength = types.MaxIdentityLength
|
||||
MaxWebsiteLength = types.MaxWebsiteLength
|
||||
|
@ -86,6 +88,10 @@ var (
|
|||
NewDelegationResp = types.NewDelegationResp
|
||||
NewRedelegationResponse = types.NewRedelegationResponse
|
||||
NewRedelegationEntryResponse = types.NewRedelegationEntryResponse
|
||||
NewHistoricalInfo = types.NewHistoricalInfo
|
||||
MustMarshalHistoricalInfo = types.MustMarshalHistoricalInfo
|
||||
MustUnmarshalHistoricalInfo = types.MustUnmarshalHistoricalInfo
|
||||
UnmarshalHistoricalInfo = types.UnmarshalHistoricalInfo
|
||||
ErrNilValidatorAddr = types.ErrNilValidatorAddr
|
||||
ErrBadValidatorAddr = types.ErrBadValidatorAddr
|
||||
ErrNoValidatorFound = types.ErrNoValidatorFound
|
||||
|
@ -131,6 +137,8 @@ var (
|
|||
ErrBothShareMsgsGiven = types.ErrBothShareMsgsGiven
|
||||
ErrNeitherShareMsgsGiven = types.ErrNeitherShareMsgsGiven
|
||||
ErrMissingSignature = types.ErrMissingSignature
|
||||
ErrInvalidHistoricalInfo = types.ErrInvalidHistoricalInfo
|
||||
ErrNoHistoricalInfo = types.ErrNoHistoricalInfo
|
||||
NewGenesisState = types.NewGenesisState
|
||||
DefaultGenesisState = types.DefaultGenesisState
|
||||
NewMultiStakingHooks = types.NewMultiStakingHooks
|
||||
|
@ -159,6 +167,7 @@ var (
|
|||
GetREDsFromValSrcIndexKey = types.GetREDsFromValSrcIndexKey
|
||||
GetREDsToValDstIndexKey = types.GetREDsToValDstIndexKey
|
||||
GetREDsByDelToValDstIndexKey = types.GetREDsByDelToValDstIndexKey
|
||||
GetHistoricalInfoKey = types.GetHistoricalInfoKey
|
||||
NewMsgCreateValidator = types.NewMsgCreateValidator
|
||||
NewMsgEditValidator = types.NewMsgEditValidator
|
||||
NewMsgDelegate = types.NewMsgDelegate
|
||||
|
@ -174,6 +183,7 @@ var (
|
|||
NewQueryBondsParams = types.NewQueryBondsParams
|
||||
NewQueryRedelegationParams = types.NewQueryRedelegationParams
|
||||
NewQueryValidatorsParams = types.NewQueryValidatorsParams
|
||||
NewQueryHistoricalInfoParams = types.NewQueryHistoricalInfoParams
|
||||
NewValidator = types.NewValidator
|
||||
MustMarshalValidator = types.MustMarshalValidator
|
||||
MustUnmarshalValidator = types.MustUnmarshalValidator
|
||||
|
@ -196,6 +206,7 @@ var (
|
|||
UnbondingQueueKey = types.UnbondingQueueKey
|
||||
RedelegationQueueKey = types.RedelegationQueueKey
|
||||
ValidatorQueueKey = types.ValidatorQueueKey
|
||||
HistoricalInfoKey = types.HistoricalInfoKey
|
||||
KeyUnbondingTime = types.KeyUnbondingTime
|
||||
KeyMaxValidators = types.KeyMaxValidators
|
||||
KeyMaxEntries = types.KeyMaxEntries
|
||||
|
@ -216,6 +227,7 @@ type (
|
|||
Redelegation = types.Redelegation
|
||||
RedelegationEntry = types.RedelegationEntry
|
||||
Redelegations = types.Redelegations
|
||||
HistoricalInfo = types.HistoricalInfo
|
||||
DelegationResponse = types.DelegationResponse
|
||||
DelegationResponses = types.DelegationResponses
|
||||
RedelegationResponse = types.RedelegationResponse
|
||||
|
@ -237,6 +249,7 @@ type (
|
|||
QueryBondsParams = types.QueryBondsParams
|
||||
QueryRedelegationParams = types.QueryRedelegationParams
|
||||
QueryValidatorsParams = types.QueryValidatorsParams
|
||||
QueryHistoricalInfoParams = types.QueryHistoricalInfoParams
|
||||
Validator = types.Validator
|
||||
Validators = types.Validators
|
||||
Description = types.Description
|
||||
|
|
|
@ -2,6 +2,7 @@ package cli
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
@ -35,6 +36,7 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
|||
GetCmdQueryValidatorDelegations(queryRoute, cdc),
|
||||
GetCmdQueryValidatorUnbondingDelegations(queryRoute, cdc),
|
||||
GetCmdQueryValidatorRedelegations(queryRoute, cdc),
|
||||
GetCmdQueryHistoricalInfo(queryRoute, cdc),
|
||||
GetCmdQueryParams(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.
|
||||
func GetCmdQueryPool(storeName string, cdc *codec.Codec) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
|
|
|
@ -3,6 +3,7 @@ package rest
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
@ -86,6 +87,12 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router) {
|
|||
validatorUnbondingDelegationsHandlerFn(cliCtx),
|
||||
).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
|
||||
r.HandleFunc(
|
||||
"/staking/pool",
|
||||
|
@ -313,6 +320,36 @@ func validatorUnbondingDelegationsHandlerFn(cliCtx context.CLIContext) http.Hand
|
|||
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
|
||||
func poolHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/libs/common"
|
||||
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,
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
func (k Keeper) BondDenom(ctx sdk.Context) (res string) {
|
||||
k.paramstore.Get(ctx, types.KeyBondDenom, &res)
|
||||
|
@ -49,6 +56,7 @@ func (k Keeper) GetParams(ctx sdk.Context) types.Params {
|
|||
k.UnbondingTime(ctx),
|
||||
k.MaxValidators(ctx),
|
||||
k.MaxEntries(ctx),
|
||||
k.HistoricalEntries(ctx),
|
||||
k.BondDenom(ctx),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -38,6 +38,8 @@ func NewQuerier(k Keeper) sdk.Querier {
|
|||
return queryDelegatorValidators(ctx, req, k)
|
||||
case types.QueryDelegatorValidator:
|
||||
return queryDelegatorValidator(ctx, req, k)
|
||||
case types.QueryHistoricalInfo:
|
||||
return queryHistoricalInfo(ctx, req, k)
|
||||
case types.QueryPool:
|
||||
return queryPool(ctx, k)
|
||||
case types.QueryParameters:
|
||||
|
@ -327,6 +329,27 @@ func queryRedelegations(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byt
|
|||
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) {
|
||||
bondDenom := k.BondDenom(ctx)
|
||||
bondedPool := k.GetBondedPool(ctx)
|
||||
|
|
|
@ -31,6 +31,13 @@ func TestNewQuerier(t *testing.T) {
|
|||
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{
|
||||
Path: "",
|
||||
Data: []byte{},
|
||||
|
@ -39,53 +46,63 @@ func TestNewQuerier(t *testing.T) {
|
|||
querier := NewQuerier(keeper)
|
||||
|
||||
bz, err := querier(ctx, []string{"other"}, query)
|
||||
require.NotNil(t, err)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, bz)
|
||||
|
||||
_, err = querier(ctx, []string{"pool"}, query)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = querier(ctx, []string{"parameters"}, query)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
queryValParams := types.NewQueryValidatorParams(addrVal1)
|
||||
bz, errRes := cdc.MarshalJSON(queryValParams)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
|
||||
query.Path = "/custom/staking/validator"
|
||||
query.Data = bz
|
||||
|
||||
_, err = querier(ctx, []string{"validator"}, query)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = querier(ctx, []string{"validatorDelegations"}, query)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = querier(ctx, []string{"validatorUnbondingDelegations"}, query)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
queryDelParams := types.NewQueryDelegatorParams(addrAcc2)
|
||||
bz, errRes = cdc.MarshalJSON(queryDelParams)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
|
||||
query.Path = "/custom/staking/validator"
|
||||
query.Data = bz
|
||||
|
||||
_, err = querier(ctx, []string{"delegatorDelegations"}, query)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = querier(ctx, []string{"delegatorUnbondingDelegations"}, query)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = querier(ctx, []string{"delegatorValidators"}, query)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
bz, errRes = cdc.MarshalJSON(types.NewQueryRedelegationParams(nil, nil, nil))
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
query.Data = bz
|
||||
|
||||
_, 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) {
|
||||
|
@ -94,21 +111,21 @@ func TestQueryParametersPool(t *testing.T) {
|
|||
bondDenom := sdk.DefaultBondDenom
|
||||
|
||||
res, err := queryParameters(ctx, keeper)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var params types.Params
|
||||
errRes := cdc.UnmarshalJSON(res, ¶ms)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
require.Equal(t, keeper.GetParams(ctx), params)
|
||||
|
||||
res, err = queryPool(ctx, keeper)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var pool types.Pool
|
||||
bondedPool := keeper.GetBondedPool(ctx)
|
||||
notBondedPool := keeper.GetNotBondedPool(ctx)
|
||||
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, notBondedPool.GetCoins().AmountOf(bondDenom), pool.NotBondedTokens)
|
||||
}
|
||||
|
@ -138,7 +155,7 @@ func TestQueryValidators(t *testing.T) {
|
|||
for i, s := range status {
|
||||
queryValsParams := types.NewQueryValidatorsParams(1, int(params.MaxValidators), s.String())
|
||||
bz, err := cdc.MarshalJSON(queryValsParams)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
req := abci.RequestQuery{
|
||||
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)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var validatorsResp []types.Validator
|
||||
err = cdc.UnmarshalJSON(res, &validatorsResp)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(validatorsResp))
|
||||
require.ElementsMatch(t, validators[i].OperatorAddress, validatorsResp[0].OperatorAddress)
|
||||
|
@ -160,18 +177,18 @@ func TestQueryValidators(t *testing.T) {
|
|||
// Query each validator
|
||||
queryParams := types.NewQueryValidatorParams(addrVal1)
|
||||
bz, err := cdc.MarshalJSON(queryParams)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
query := abci.RequestQuery{
|
||||
Path: "/custom/staking/validator",
|
||||
Data: bz,
|
||||
}
|
||||
res, err := queryValidator(ctx, query, keeper)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var validator types.Validator
|
||||
err = cdc.UnmarshalJSON(res, &validator)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, queriedValidators[0], validator)
|
||||
}
|
||||
|
@ -199,7 +216,7 @@ func TestQueryDelegation(t *testing.T) {
|
|||
// Query Delegator bonded validators
|
||||
queryParams := types.NewQueryDelegatorParams(addrAcc2)
|
||||
bz, errRes := cdc.MarshalJSON(queryParams)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
|
||||
query := abci.RequestQuery{
|
||||
Path: "/custom/staking/delegatorValidators",
|
||||
|
@ -209,11 +226,11 @@ func TestQueryDelegation(t *testing.T) {
|
|||
delValidators := keeper.GetDelegatorValidators(ctx, addrAcc2, params.MaxValidators)
|
||||
|
||||
res, err := queryDelegatorValidators(ctx, query, keeper)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var validatorsResp []types.Validator
|
||||
errRes = cdc.UnmarshalJSON(res, &validatorsResp)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
|
||||
require.Equal(t, len(delValidators), len(validatorsResp))
|
||||
require.ElementsMatch(t, delValidators, validatorsResp)
|
||||
|
@ -222,12 +239,12 @@ func TestQueryDelegation(t *testing.T) {
|
|||
query.Data = bz[:len(bz)-1]
|
||||
|
||||
_, err = queryDelegatorValidators(ctx, query, keeper)
|
||||
require.NotNil(t, err)
|
||||
require.Error(t, err)
|
||||
|
||||
// Query bonded validator
|
||||
queryBondParams := types.NewQueryBondsParams(addrAcc2, addrVal1)
|
||||
bz, errRes = cdc.MarshalJSON(queryBondParams)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
|
||||
query = abci.RequestQuery{
|
||||
Path: "/custom/staking/delegatorValidator",
|
||||
|
@ -235,11 +252,11 @@ func TestQueryDelegation(t *testing.T) {
|
|||
}
|
||||
|
||||
res, err = queryDelegatorValidator(ctx, query, keeper)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var validator types.Validator
|
||||
errRes = cdc.UnmarshalJSON(res, &validator)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
|
||||
require.Equal(t, delValidators[0], validator)
|
||||
|
||||
|
@ -247,7 +264,7 @@ func TestQueryDelegation(t *testing.T) {
|
|||
query.Data = bz[:len(bz)-1]
|
||||
|
||||
_, err = queryDelegatorValidator(ctx, query, keeper)
|
||||
require.NotNil(t, err)
|
||||
require.Error(t, err)
|
||||
|
||||
// Query delegation
|
||||
|
||||
|
@ -260,11 +277,11 @@ func TestQueryDelegation(t *testing.T) {
|
|||
require.True(t, found)
|
||||
|
||||
res, err = queryDelegation(ctx, query, keeper)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var delegationRes types.DelegationResponse
|
||||
errRes = cdc.UnmarshalJSON(res, &delegationRes)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
|
||||
require.Equal(t, delegation.ValidatorAddress, delegationRes.ValidatorAddress)
|
||||
require.Equal(t, delegation.DelegatorAddress, delegationRes.DelegatorAddress)
|
||||
|
@ -277,11 +294,11 @@ func TestQueryDelegation(t *testing.T) {
|
|||
}
|
||||
|
||||
res, err = queryDelegatorDelegations(ctx, query, keeper)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var delegatorDelegations types.DelegationResponses
|
||||
errRes = cdc.UnmarshalJSON(res, &delegatorDelegations)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
require.Len(t, delegatorDelegations, 1)
|
||||
require.Equal(t, delegation.ValidatorAddress, delegatorDelegations[0].ValidatorAddress)
|
||||
require.Equal(t, delegation.DelegatorAddress, delegatorDelegations[0].DelegatorAddress)
|
||||
|
@ -291,12 +308,12 @@ func TestQueryDelegation(t *testing.T) {
|
|||
query.Data = bz[:len(bz)-1]
|
||||
|
||||
_, err = queryDelegation(ctx, query, keeper)
|
||||
require.NotNil(t, err)
|
||||
require.Error(t, err)
|
||||
|
||||
// Query validator delegations
|
||||
|
||||
bz, errRes = cdc.MarshalJSON(types.NewQueryValidatorParams(addrVal1))
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
|
||||
query = abci.RequestQuery{
|
||||
Path: "custom/staking/validatorDelegations",
|
||||
|
@ -304,11 +321,11 @@ func TestQueryDelegation(t *testing.T) {
|
|||
}
|
||||
|
||||
res, err = queryValidatorDelegations(ctx, query, keeper)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var delegationsRes types.DelegationResponses
|
||||
errRes = cdc.UnmarshalJSON(res, &delegationsRes)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
require.Len(t, delegatorDelegations, 1)
|
||||
require.Equal(t, delegation.ValidatorAddress, delegationsRes[0].ValidatorAddress)
|
||||
require.Equal(t, delegation.DelegatorAddress, delegationsRes[0].DelegatorAddress)
|
||||
|
@ -317,11 +334,11 @@ func TestQueryDelegation(t *testing.T) {
|
|||
// Query unbonging delegation
|
||||
unbondingTokens := sdk.TokensFromConsensusPower(10)
|
||||
_, err = keeper.Undelegate(ctx, addrAcc2, val1.OperatorAddress, unbondingTokens.ToDec())
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
queryBondParams = types.NewQueryBondsParams(addrAcc2, addrVal1)
|
||||
bz, errRes = cdc.MarshalJSON(queryBondParams)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
|
||||
query = abci.RequestQuery{
|
||||
Path: "/custom/staking/unbondingDelegation",
|
||||
|
@ -332,11 +349,11 @@ func TestQueryDelegation(t *testing.T) {
|
|||
require.True(t, found)
|
||||
|
||||
res, err = queryUnbondingDelegation(ctx, query, keeper)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var unbondRes types.UnbondingDelegation
|
||||
errRes = cdc.UnmarshalJSON(res, &unbondRes)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
|
||||
require.Equal(t, unbond, unbondRes)
|
||||
|
||||
|
@ -344,7 +361,7 @@ func TestQueryDelegation(t *testing.T) {
|
|||
query.Data = bz[:len(bz)-1]
|
||||
|
||||
_, err = queryUnbondingDelegation(ctx, query, keeper)
|
||||
require.NotNil(t, err)
|
||||
require.Error(t, err)
|
||||
|
||||
// Query Delegator Delegations
|
||||
|
||||
|
@ -354,29 +371,29 @@ func TestQueryDelegation(t *testing.T) {
|
|||
}
|
||||
|
||||
res, err = queryDelegatorUnbondingDelegations(ctx, query, keeper)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var delegatorUbds []types.UnbondingDelegation
|
||||
errRes = cdc.UnmarshalJSON(res, &delegatorUbds)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
require.Equal(t, unbond, delegatorUbds[0])
|
||||
|
||||
// error unknown request
|
||||
query.Data = bz[:len(bz)-1]
|
||||
|
||||
_, err = queryDelegatorUnbondingDelegations(ctx, query, keeper)
|
||||
require.NotNil(t, err)
|
||||
require.Error(t, err)
|
||||
|
||||
// Query redelegation
|
||||
redelegationTokens := sdk.TokensFromConsensusPower(10)
|
||||
_, err = keeper.BeginRedelegation(ctx, addrAcc2, val1.OperatorAddress,
|
||||
val2.OperatorAddress, redelegationTokens.ToDec())
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
redel, found := keeper.GetRedelegation(ctx, addrAcc2, val1.OperatorAddress, val2.OperatorAddress)
|
||||
require.True(t, found)
|
||||
|
||||
bz, errRes = cdc.MarshalJSON(types.NewQueryRedelegationParams(addrAcc2, val1.OperatorAddress, val2.OperatorAddress))
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
|
||||
query = abci.RequestQuery{
|
||||
Path: "/custom/staking/redelegations",
|
||||
|
@ -384,11 +401,11 @@ func TestQueryDelegation(t *testing.T) {
|
|||
}
|
||||
|
||||
res, err = queryRedelegations(ctx, query, keeper)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var redelRes types.RedelegationResponses
|
||||
errRes = cdc.UnmarshalJSON(res, &redelRes)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
require.Len(t, redelRes, 1)
|
||||
require.Equal(t, redel.DelegatorAddress, redelRes[0].DelegatorAddress)
|
||||
require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].ValidatorSrcAddress)
|
||||
|
@ -420,7 +437,7 @@ func TestQueryRedelegations(t *testing.T) {
|
|||
// delegator redelegations
|
||||
queryDelegatorParams := types.NewQueryDelegatorParams(addrAcc2)
|
||||
bz, errRes := cdc.MarshalJSON(queryDelegatorParams)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
|
||||
query := abci.RequestQuery{
|
||||
Path: "/custom/staking/redelegations",
|
||||
|
@ -428,11 +445,11 @@ func TestQueryRedelegations(t *testing.T) {
|
|||
}
|
||||
|
||||
res, err := queryRedelegations(ctx, query, keeper)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var redelRes types.RedelegationResponses
|
||||
errRes = cdc.UnmarshalJSON(res, &redelRes)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
require.Len(t, redelRes, 1)
|
||||
require.Equal(t, redel.DelegatorAddress, redelRes[0].DelegatorAddress)
|
||||
require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].ValidatorSrcAddress)
|
||||
|
@ -442,7 +459,7 @@ func TestQueryRedelegations(t *testing.T) {
|
|||
// validator redelegations
|
||||
queryValidatorParams := types.NewQueryValidatorParams(val1.GetOperator())
|
||||
bz, errRes = cdc.MarshalJSON(queryValidatorParams)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
|
||||
query = abci.RequestQuery{
|
||||
Path: "/custom/staking/redelegations",
|
||||
|
@ -450,10 +467,10 @@ func TestQueryRedelegations(t *testing.T) {
|
|||
}
|
||||
|
||||
res, err = queryRedelegations(ctx, query, keeper)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
errRes = cdc.UnmarshalJSON(res, &redelRes)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
require.Len(t, redelRes, 1)
|
||||
require.Equal(t, redel.DelegatorAddress, redelRes[0].DelegatorAddress)
|
||||
require.Equal(t, redel.ValidatorSrcAddress, redelRes[0].ValidatorSrcAddress)
|
||||
|
@ -489,13 +506,13 @@ func TestQueryUnbondingDelegation(t *testing.T) {
|
|||
//
|
||||
queryValidatorParams := types.NewQueryBondsParams(addrAcc1, val1.GetOperator())
|
||||
bz, errRes := cdc.MarshalJSON(queryValidatorParams)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
query := abci.RequestQuery{
|
||||
Path: "/custom/staking/unbondingDelegation",
|
||||
Data: bz,
|
||||
}
|
||||
res, err := queryUnbondingDelegation(ctx, query, keeper)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
var ubDel types.UnbondingDelegation
|
||||
require.NoError(t, cdc.UnmarshalJSON(res, &ubDel))
|
||||
|
@ -508,26 +525,26 @@ func TestQueryUnbondingDelegation(t *testing.T) {
|
|||
//
|
||||
queryValidatorParams = types.NewQueryBondsParams(addrAcc2, val1.GetOperator())
|
||||
bz, errRes = cdc.MarshalJSON(queryValidatorParams)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
query = abci.RequestQuery{
|
||||
Path: "/custom/staking/unbondingDelegation",
|
||||
Data: bz,
|
||||
}
|
||||
_, err = queryUnbondingDelegation(ctx, query, keeper)
|
||||
require.NotNil(t, err)
|
||||
require.Error(t, err)
|
||||
|
||||
//
|
||||
// found: query unbonding delegation by delegator and validator
|
||||
//
|
||||
queryDelegatorParams := types.NewQueryDelegatorParams(addrAcc1)
|
||||
bz, errRes = cdc.MarshalJSON(queryDelegatorParams)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
query = abci.RequestQuery{
|
||||
Path: "/custom/staking/delegatorUnbondingDelegations",
|
||||
Data: bz,
|
||||
}
|
||||
res, err = queryDelegatorUnbondingDelegations(ctx, query, keeper)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
var ubDels []types.UnbondingDelegation
|
||||
require.NoError(t, cdc.UnmarshalJSON(res, &ubDels))
|
||||
|
@ -540,14 +557,56 @@ func TestQueryUnbondingDelegation(t *testing.T) {
|
|||
//
|
||||
queryDelegatorParams = types.NewQueryDelegatorParams(addrAcc2)
|
||||
bz, errRes = cdc.MarshalJSON(queryDelegatorParams)
|
||||
require.Nil(t, errRes)
|
||||
require.NoError(t, errRes)
|
||||
query = abci.RequestQuery{
|
||||
Path: "/custom/staking/delegatorUnbondingDelegations",
|
||||
Data: bz,
|
||||
}
|
||||
res, err = queryDelegatorUnbondingDelegations(ctx, query, keeper)
|
||||
require.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
require.NoError(t, cdc.UnmarshalJSON(res, &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"
|
||||
)
|
||||
|
||||
// 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,
|
||||
// * Updates the active valset as keyed by LastValidatorPowerKey.
|
||||
// * 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.
|
||||
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
|
||||
// updates.
|
||||
|
|
|
@ -50,7 +50,7 @@ func RandomizedGenState(simState *module.SimulationState) {
|
|||
// NewSimulationManager constructor for this to work
|
||||
simState.UnbondTime = unbondTime
|
||||
|
||||
params := types.NewParams(simState.UnbondTime, maxValidators, 7, sdk.DefaultBondDenom)
|
||||
params := types.NewParams(simState.UnbondTime, maxValidators, 7, 3, sdk.DefaultBondDenom)
|
||||
|
||||
// validators & delegations
|
||||
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
|
||||
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.
|
||||
|
||||
## 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
|
|
@ -1,5 +1,5 @@
|
|||
<!--
|
||||
order: 5
|
||||
order: 6
|
||||
-->
|
||||
|
||||
# Hooks
|
|
@ -1,5 +1,5 @@
|
|||
<!--
|
||||
order: 6
|
||||
order: 7
|
||||
-->
|
||||
|
||||
# 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)
|
||||
- [Redelegation](01_state.md#redelegation)
|
||||
- [Queues](01_state.md#queues)
|
||||
- [HistoricalInfo](01_state.md#historicalinfo)
|
||||
2. **[State Transitions](02_state_transitions.md)**
|
||||
- [Validators](02_state_transitions.md#validators)
|
||||
- [Delegations](02_state_transitions.md#delegations)
|
||||
|
@ -42,11 +43,13 @@ network.
|
|||
- [MsgDelegate](03_messages.md#msgdelegate)
|
||||
- [MsgBeginUnbonding](03_messages.md#msgbeginunbonding)
|
||||
- [MsgBeginRedelegate](03_messages.md#msgbeginredelegate)
|
||||
4. **[End-Block ](04_end_block.md)**
|
||||
- [Validator Set Changes](04_end_block.md#validator-set-changes)
|
||||
- [Queues ](04_end_block.md#queues-)
|
||||
5. **[Hooks](05_hooks.md)**
|
||||
6. **[Events](06_events.md)**
|
||||
- [EndBlocker](06_events.md#endblocker)
|
||||
- [Handlers](06_events.md#handlers)
|
||||
7. **[Parameters](07_params.md)**
|
||||
4. **[Begin-Block](04_begin_block.md)**
|
||||
- [Historical Info Tracking](04_begin_block.md#historical-info-tracking)
|
||||
4. **[End-Block ](05_end_block.md)**
|
||||
- [Validator Set Changes](05_end_block.md#validator-set-changes)
|
||||
- [Queues ](05_end_block.md#queues-)
|
||||
5. **[Hooks](06_hooks.md)**
|
||||
6. **[Events](07_events.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 (
|
||||
DefaultCodespace sdk.CodespaceType = ModuleName
|
||||
|
||||
CodeInvalidValidator CodeType = 101
|
||||
CodeInvalidDelegation CodeType = 102
|
||||
CodeInvalidInput CodeType = 103
|
||||
CodeValidatorJailed CodeType = 104
|
||||
CodeInvalidAddress CodeType = sdk.CodeInvalidAddress
|
||||
CodeUnauthorized CodeType = sdk.CodeUnauthorized
|
||||
CodeInternal CodeType = sdk.CodeInternal
|
||||
CodeUnknownRequest CodeType = sdk.CodeUnknownRequest
|
||||
CodeInvalidValidator CodeType = 101
|
||||
CodeInvalidDelegation CodeType = 102
|
||||
CodeInvalidInput CodeType = 103
|
||||
CodeValidatorJailed CodeType = 104
|
||||
CodeInvalidHistoricalInfo CodeType = 105
|
||||
CodeInvalidAddress CodeType = sdk.CodeInvalidAddress
|
||||
CodeUnauthorized CodeType = sdk.CodeUnauthorized
|
||||
CodeInternal CodeType = sdk.CodeInternal
|
||||
CodeUnknownRequest CodeType = sdk.CodeUnknownRequest
|
||||
)
|
||||
|
||||
//validator
|
||||
|
@ -212,3 +213,11 @@ func ErrNeitherShareMsgsGiven(codespace sdk.CodespaceType) sdk.Error {
|
|||
func ErrMissingSignature(codespace sdk.CodespaceType) sdk.Error {
|
||||
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 (
|
||||
"encoding/binary"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
@ -45,6 +46,8 @@ var (
|
|||
UnbondingQueueKey = []byte{0x41} // prefix for the timestamps in unbonding queue
|
||||
RedelegationQueueKey = []byte{0x42} // prefix for the timestamps in redelegations 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
|
||||
|
@ -278,3 +281,10 @@ func GetREDsByDelToValDstIndexKey(delAddr sdk.AccAddress, valDstAddr sdk.ValAddr
|
|||
GetREDsToValDstIndexKey(valDstAddr),
|
||||
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
|
||||
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
|
||||
var (
|
||||
KeyUnbondingTime = []byte("UnbondingTime")
|
||||
KeyMaxValidators = []byte("MaxValidators")
|
||||
KeyMaxEntries = []byte("KeyMaxEntries")
|
||||
KeyBondDenom = []byte("BondDenom")
|
||||
KeyUnbondingTime = []byte("UnbondingTime")
|
||||
KeyMaxValidators = []byte("MaxValidators")
|
||||
KeyMaxEntries = []byte("KeyMaxEntries")
|
||||
KeyBondDenom = []byte("BondDenom")
|
||||
KeyHistoricalEntries = []byte("HistoricalEntries")
|
||||
)
|
||||
|
||||
var _ params.ParamSet = (*Params)(nil)
|
||||
|
||||
// Params defines the high level settings for staking
|
||||
type Params struct {
|
||||
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)
|
||||
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
|
||||
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)
|
||||
MaxEntries uint16 `json:"max_entries" yaml:"max_entries"` // max entries for either unbonding delegation or redelegation (per pair/trio)
|
||||
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
|
||||
func NewParams(unbondingTime time.Duration, maxValidators, maxEntries uint16,
|
||||
func NewParams(unbondingTime time.Duration, maxValidators, maxEntries, historicalEntries uint16,
|
||||
bondDenom string) Params {
|
||||
|
||||
return Params{
|
||||
UnbondingTime: unbondingTime,
|
||||
MaxValidators: maxValidators,
|
||||
MaxEntries: maxEntries,
|
||||
BondDenom: bondDenom,
|
||||
UnbondingTime: unbondingTime,
|
||||
MaxValidators: maxValidators,
|
||||
MaxEntries: maxEntries,
|
||||
HistoricalEntries: historicalEntries,
|
||||
BondDenom: bondDenom,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,6 +69,7 @@ func (p *Params) ParamSetPairs() params.ParamSetPairs {
|
|||
params.NewParamSetPair(KeyUnbondingTime, &p.UnbondingTime, validateUnbondingTime),
|
||||
params.NewParamSetPair(KeyMaxValidators, &p.MaxValidators, validateMaxValidators),
|
||||
params.NewParamSetPair(KeyMaxEntries, &p.MaxEntries, validateMaxEntries),
|
||||
params.NewParamSetPair(KeyHistoricalEntries, &p.HistoricalEntries, validateHistoricalEntries),
|
||||
params.NewParamSetPair(KeyBondDenom, &p.BondDenom, validateBondDenom),
|
||||
}
|
||||
}
|
||||
|
@ -76,17 +84,18 @@ func (p Params) Equal(p2 Params) bool {
|
|||
|
||||
// DefaultParams returns a default set of parameters.
|
||||
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.
|
||||
func (p Params) String() string {
|
||||
return fmt.Sprintf(`Params:
|
||||
Unbonding Time: %s
|
||||
Max Validators: %d
|
||||
Max Entries: %d
|
||||
Bonded Coin Denom: %s`, p.UnbondingTime,
|
||||
p.MaxValidators, p.MaxEntries, p.BondDenom)
|
||||
Unbonding Time: %s
|
||||
Max Validators: %d
|
||||
Max Entries: %d
|
||||
Historical Entries: %d
|
||||
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
|
||||
|
@ -164,6 +173,15 @@ func validateMaxEntries(i interface{}) error {
|
|||
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 {
|
||||
v, ok := i.(string)
|
||||
if !ok {
|
||||
|
|
|
@ -20,6 +20,7 @@ const (
|
|||
QueryDelegatorValidator = "delegatorValidator"
|
||||
QueryPool = "pool"
|
||||
QueryParameters = "parameters"
|
||||
QueryHistoricalInfo = "historicalInfo"
|
||||
)
|
||||
|
||||
// defines the params for the following queries:
|
||||
|
@ -96,3 +97,14 @@ type QueryValidatorsParams struct {
|
|||
func NewQueryValidatorsParams(page, limit int, status string) QueryValidatorsParams {
|
||||
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 (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -102,6 +103,28 @@ func (v Validators) ToSDKValidators() (validators []exported.ValidatorI) {
|
|||
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
|
||||
func NewValidator(operator sdk.ValAddress, pubKey crypto.PubKey, description Description) Validator {
|
||||
return Validator{
|
||||
|
|
|
@ -2,10 +2,14 @@ package types
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
|
||||
|
@ -303,3 +307,31 @@ func TestValidatorMarshalYAML(t *testing.T) {
|
|||
`, validator.OperatorAddress.String(), bechifiedPub)
|
||||
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