Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ADR 17 Implementation: Historical Module #5380

Merged
merged 31 commits into from
Dec 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
17fcad3
add history module and some tests
AdityaSripal Dec 10, 2019
5f916a4
Merge branch 'aditya/history' of https://github.com/cosmos/cosmos-sdk…
AdityaSripal Dec 10, 2019
2b052b4
complete query and write tests
AdityaSripal Dec 10, 2019
481ee0d
complete query and write tests
AdityaSripal Dec 10, 2019
954c8b1
add abci test
AdityaSripal Dec 10, 2019
da266e8
docs and CHANGELOG
AdityaSripal Dec 10, 2019
813c634
add client query methods and CHANGELOG
AdityaSripal Dec 11, 2019
5c634a3
fix merge
AdityaSripal Dec 11, 2019
33154f0
Merge branch 'master' into aditya/history
cwgoes Dec 11, 2019
aa34dc7
implement edge case for when historicalentries param gets reduced
AdityaSripal Dec 11, 2019
fb2e660
Merge branch 'aditya/history' of https://github.com/cosmos/cosmos-sdk…
AdityaSripal Dec 11, 2019
d0e8d88
Update x/staking/abci.go
cwgoes Dec 11, 2019
5997e15
Apply suggestions from code review
AdityaSripal Dec 12, 2019
82d386f
apply codereview suggestions
AdityaSripal Dec 12, 2019
a45a5fa
appease linter
AdityaSripal Dec 13, 2019
ef43df5
Update x/staking/client/cli/query.go
fedekunze Dec 16, 2019
093dcf5
Merge branch 'master' into aditya/history
fedekunze Dec 16, 2019
c3fc30b
Apply suggestions from code review
AdityaSripal Dec 16, 2019
e4d2c34
Update x/staking/keeper/historical_info.go
AdityaSripal Dec 16, 2019
74b3f19
update spec, alias, and fix minor bug
AdityaSripal Dec 17, 2019
73cbb7e
cleanup changelog
AdityaSripal Dec 17, 2019
277054b
Merge branch 'master' into aditya/history
AdityaSripal Dec 17, 2019
a61d28a
Merge branch 'master' of https://github.com/cosmos/cosmos-sdk into ad…
AdityaSripal Dec 17, 2019
1fdc269
Merge branch 'aditya/history' of https://github.com/cosmos/cosmos-sdk…
AdityaSripal Dec 17, 2019
3f59d8c
fix merge
AdityaSripal Dec 17, 2019
9364ba4
Make BeginBlocker/EndBlocker methods on keeper
AdityaSripal Dec 17, 2019
c0f2ac7
Merge branch 'master' into aditya/history
alexanderbez Dec 17, 2019
4402252
move beginblock/endblock logic into keeper, maintain API
AdityaSripal Dec 18, 2019
c3ad793
remove redundant abci tests
AdityaSripal Dec 18, 2019
b1a9ac5
Merge branch 'aditya/history' of https://github.com/cosmos/cosmos-sdk…
AdityaSripal Dec 18, 2019
ffc64bf
Merge branch 'master' into aditya/history
AdityaSripal Dec 18, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A single changelog entry should be made with bullet points if need be.

* 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.

Expand Down
18 changes: 18 additions & 0 deletions x/staking/abci.go
Original file line number Diff line number Diff line change
@@ -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)
}
13 changes: 13 additions & 0 deletions x/staking/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
46 changes: 46 additions & 0 deletions x/staking/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cli

import (
"fmt"
"strconv"
"strings"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -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))...)

Expand Down Expand Up @@ -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{
Expand Down
37 changes: 37 additions & 0 deletions x/staking/client/rest/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package rest
import (
"fmt"
"net/http"
"strconv"
"strings"

"github.com/gorilla/mux"
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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) {
Expand Down
56 changes: 0 additions & 56 deletions x/staking/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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

Expand Down
71 changes: 71 additions & 0 deletions x/staking/keeper/historical_info.go
Original file line number Diff line number Diff line change
@@ -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)
}
Loading