diff --git a/Makefile b/Makefile index 983234dbc3c8..94ab30e50522 100644 --- a/Makefile +++ b/Makefile @@ -160,7 +160,7 @@ test_sim_gaia_fast: @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=50 -v -timeout 24h test_sim_gaia_slow: - @echo "Running full Gaia simulation. This may take awhile!" + @echo "Running full Gaia simulation. This may take a while!" @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=1000 -SimulationVerbose=true -v -timeout 24h test_cover: diff --git a/PENDING.md b/PENDING.md index 7eb324a23d77..98cc8758583f 100644 --- a/PENDING.md +++ b/PENDING.md @@ -26,9 +26,8 @@ BREAKING CHANGES * [x/stake] [#1676] Revoked and jailed validators put into the unbonding state * [x/stake] [#1877] Redelegations/unbonding-delegation from unbonding validator have reduced time * [x/stake] \#2040 Validator operator type has now changed to `sdk.ValAddress` - * A new bech32 prefix has been introduced for Tendermint signing keys and - addresses, `cosmosconspub` and `cosmoscons` respectively. - + * A new bech32 prefix has been introduced for Tendermint signing keys and addresses, `cosmosconspub` and `cosmoscons` respectively. + * SDK * [core] \#1807 Switch from use of rational to decimal * [types] \#1901 Validator interface's GetOwner() renamed to GetOperator() @@ -85,6 +84,7 @@ IMPROVEMENTS * [cli] \#1632 Add integration tests to ensure `basecoind init && basecoind` start sequences run successfully for both `democoin` and `basecoin` examples. * [store] Speedup IAVL iteration, and consequently everything that requires IAVL iteration. [#2143](https://github.com/cosmos/cosmos-sdk/issues/2143) * [simulation] Make timestamps randomized [#2153](https://github.com/cosmos/cosmos-sdk/pull/2153) + * [x/stake] \#2139 Add stake queriers for increased performance of Gaia-lite endpoints * Tendermint diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 707cc21f7a0c..e7a504bf8214 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -29,7 +29,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/stake" - "github.com/cosmos/cosmos-sdk/x/stake/client/rest" ) func init() { @@ -417,12 +416,9 @@ func TestValidatorsQuery(t *testing.T) { require.Equal(t, len(validators), 1) // make sure all the validators were found (order unknown because sorted by operator addr) - foundVal := false - pkBech := sdk.MustBech32ifyConsPub(pks[0]) - if validators[0].PubKey == pkBech { - foundVal = true - } - require.True(t, foundVal, "pkBech %v, operator %v", pkBech, validators[0].Operator) + pkBech, err := sdk.Bech32ifyConsPub(pks[0]) + require.Nil(t, err) + require.Equal(t, pkBech, validators[0].PubKey) } func TestValidatorQuery(t *testing.T) { @@ -432,7 +428,7 @@ func TestValidatorQuery(t *testing.T) { validator1Operator := sdk.ValAddress(pks[0].Address()) validator := getValidator(t, port, validator1Operator) - assert.Equal(t, validator.Operator, validator1Operator, "The returned validator does not hold the correct data") + assert.Equal(t, validator1Operator.String(), validator.Operator, "The returned validator does not hold the correct data") } func TestBonding(t *testing.T) { @@ -468,11 +464,11 @@ func TestBonding(t *testing.T) { bondedValidators := getDelegatorValidators(t, port, addr) require.Len(t, bondedValidators, 1) - require.Equal(t, validator1Operator, bondedValidators[0].Operator) + require.Equal(t, validator1Operator.String(), bondedValidators[0].Operator) require.Equal(t, validator.DelegatorShares.Add(sdk.NewDec(60)).String(), bondedValidators[0].DelegatorShares.String()) bondedValidator := getDelegatorValidator(t, port, addr, validator1Operator) - require.Equal(t, validator1Operator, bondedValidator.Operator) + require.Equal(t, validator1Operator.String(), bondedValidator.Operator) ////////////////////// // testing unbonding @@ -489,9 +485,8 @@ func TestBonding(t *testing.T) { coins = acc.GetCoins() require.Equal(t, int64(40), coins.AmountOf("steak").Int64()) - unbondings := getUndelegations(t, port, addr, validator1Operator) - require.Len(t, unbondings, 1, "Unbondings holds all unbonding-delegations") - require.Equal(t, "60", unbondings[0].Balance.Amount.String()) + unbonding := getUndelegation(t, port, addr, validator1Operator) + require.Equal(t, "60", unbonding.Balance.Amount.String()) summary = getDelegationSummary(t, port, addr) @@ -826,43 +821,43 @@ func doIBCTransfer(t *testing.T, port, seed, name, password string, addr sdk.Acc func getSigningInfo(t *testing.T, port string, validatorPubKey string) slashing.ValidatorSigningInfo { res, body := Request(t, port, "GET", fmt.Sprintf("/slashing/signing_info/%s", validatorPubKey), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) + var signingInfo slashing.ValidatorSigningInfo err := cdc.UnmarshalJSON([]byte(body), &signingInfo) require.Nil(t, err) + return signingInfo } // ============= Stake Module ================ -func getDelegation(t *testing.T, port string, delAddr sdk.AccAddress, valAddr sdk.ValAddress) rest.DelegationWithoutRat { - res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/delegations/%s", delAddr, valAddr), nil) +func getDelegation(t *testing.T, port string, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) stake.DelegationREST { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/delegations/%s", delegatorAddr, validatorAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) - var bond rest.DelegationWithoutRat - + var bond stake.DelegationREST err := cdc.UnmarshalJSON([]byte(body), &bond) require.Nil(t, err) return bond } -func getUndelegations(t *testing.T, port string, delAddr sdk.AccAddress, valAddr sdk.ValAddress) []stake.UnbondingDelegation { - res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/unbonding_delegations/%s", delAddr, valAddr), nil) +func getUndelegation(t *testing.T, port string, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) stake.UnbondingDelegation { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/unbonding_delegations/%s", delegatorAddr, validatorAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) - var unbondings []stake.UnbondingDelegation - + var unbondings stake.UnbondingDelegation err := cdc.UnmarshalJSON([]byte(body), &unbondings) require.Nil(t, err) return unbondings } -func getDelegationSummary(t *testing.T, port string, delegatorAddr sdk.AccAddress) rest.DelegationSummary { +func getDelegationSummary(t *testing.T, port string, delegatorAddr sdk.AccAddress) stake.DelegationSummary { res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s", delegatorAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) - var summary rest.DelegationSummary + var summary stake.DelegationSummary err := cdc.UnmarshalJSON([]byte(body), &summary) require.Nil(t, err) @@ -901,8 +896,8 @@ func getDelegatorValidators(t *testing.T, port string, delegatorAddr sdk.AccAddr return bondedValidators } -func getDelegatorValidator(t *testing.T, port string, delAddr sdk.AccAddress, valAddr sdk.ValAddress) stake.BechValidator { - res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/validators/%s", delAddr, valAddr), nil) +func getDelegatorValidator(t *testing.T, port string, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) stake.BechValidator { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/validators/%s", delegatorAddr, validatorAddr), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) var bondedValidator stake.BechValidator @@ -1028,18 +1023,22 @@ func doBeginRedelegation(t *testing.T, port, seed, name, password string, func getValidators(t *testing.T, port string) []stake.BechValidator { res, body := Request(t, port, "GET", "/stake/validators", nil) require.Equal(t, http.StatusOK, res.StatusCode, body) + var validators []stake.BechValidator err := cdc.UnmarshalJSON([]byte(body), &validators) require.Nil(t, err) + return validators } -func getValidator(t *testing.T, port string, valAddr sdk.ValAddress) stake.BechValidator { - res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s", valAddr.String()), nil) +func getValidator(t *testing.T, port string, validatorAddr sdk.ValAddress) stake.BechValidator { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s", validatorAddr.String()), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) + var validator stake.BechValidator err := cdc.UnmarshalJSON([]byte(body), &validator) require.Nil(t, err) + return validator } diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index f1ca2a7b6630..409443cb3de5 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -107,7 +107,8 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio AddRoute("gov", gov.NewHandler(app.govKeeper)) app.QueryRouter(). - AddRoute("gov", gov.NewQuerier(app.govKeeper)) + AddRoute("gov", gov.NewQuerier(app.govKeeper)). + AddRoute("stake", stake.NewQuerier(app.stakeKeeper, app.cdc)) // initialize BaseApp app.SetInitChainer(app.initChainer) diff --git a/x/gov/handler.go b/x/gov/handler.go index 6424bb0a1056..4fdcad3b8926 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -114,7 +114,7 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { resTags.AppendTag(tags.Action, tags.ActionProposalDropped) resTags.AppendTag(tags.ProposalID, proposalIDBytes) - logger.Info(fmt.Sprintf("Proposal %d - \"%s\" - didn't mean minimum deposit (had only %s), deleted", + logger.Info(fmt.Sprintf("Proposal %d (%s) didn't meet minimum deposit (had only %s), deleted", inactiveProposal.GetProposalID(), inactiveProposal.GetTitle(), inactiveProposal.GetTotalDeposit())) } @@ -143,7 +143,7 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { activeProposal.SetTallyResult(tallyResults) keeper.SetProposal(ctx, activeProposal) - logger.Info(fmt.Sprintf("Proposal %d - \"%s\" - tallied, passed: %v", + logger.Info(fmt.Sprintf("Proposal %d (%s) tallied. Passed: %v", activeProposal.GetProposalID(), activeProposal.GetTitle(), passes)) for _, valAddr := range nonVotingVals { diff --git a/x/gov/queryable.go b/x/gov/queryable.go index e64d506d1e87..9222ae1750fd 100644 --- a/x/gov/queryable.go +++ b/x/gov/queryable.go @@ -8,22 +8,33 @@ import ( abci "github.com/tendermint/tendermint/abci/types" ) +// nolint +const ( + QueryProposals = "proposals" + QueryProposal = "proposal" + QueryDeposits = "deposits" + QueryDeposit = "deposit" + QueryVotes = "votes" + QueryVote = "vote" + QueryTally = "tally" +) + func NewQuerier(keeper Keeper) sdk.Querier { return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { switch path[0] { - case "proposal": + case QueryProposals: + return queryProposals(ctx, path[1:], req, keeper) + case QueryProposal: return queryProposal(ctx, path[1:], req, keeper) - case "deposit": - return queryDeposit(ctx, path[1:], req, keeper) - case "vote": - return queryVote(ctx, path[1:], req, keeper) - case "deposits": + case QueryDeposits: return queryDeposits(ctx, path[1:], req, keeper) - case "votes": + case QueryDeposit: + return queryDeposit(ctx, path[1:], req, keeper) + case QueryVotes: return queryVotes(ctx, path[1:], req, keeper) - case "proposals": - return queryProposals(ctx, path[1:], req, keeper) - case "tally": + case QueryVote: + return queryVote(ctx, path[1:], req, keeper) + case QueryTally: return queryTally(ctx, path[1:], req, keeper) default: return nil, sdk.ErrUnknownRequest("unknown gov query endpoint") @@ -31,6 +42,30 @@ func NewQuerier(keeper Keeper) sdk.Querier { } } +// Params for query 'custom/gov/proposals' +type QueryProposalsParams struct { + Voter sdk.AccAddress + Depositer sdk.AccAddress + ProposalStatus ProposalStatus + NumLatestProposals int64 +} + +func queryProposals(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params QueryProposalsParams + errRes := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if errRes != nil { + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data: %s", errRes.Error())) + } + + proposals := keeper.GetProposalsFiltered(ctx, params.Voter, params.Depositer, params.ProposalStatus, params.NumLatestProposals) + + res, errRes = wire.MarshalJSONIndent(keeper.cdc, proposals) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) + } + return res, nil +} + // Params for query 'custom/gov/proposal' type QueryProposalParams struct { ProposalID int64 @@ -38,9 +73,9 @@ type QueryProposalParams struct { func queryProposal(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { var params QueryProposalParams - err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) - if err2 != nil { - return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) + errRes := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if errRes != nil { + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data: %s", errRes.Error())) } proposal := keeper.GetProposal(ctx, params.ProposalID) @@ -48,11 +83,11 @@ func queryProposal(ctx sdk.Context, path []string, req abci.RequestQuery, keeper return []byte{}, ErrUnknownProposal(DefaultCodespace, params.ProposalID) } - bz, err2 := wire.MarshalJSONIndent(keeper.cdc, proposal) - if err2 != nil { - panic("could not marshal result to JSON") + res, errRes = wire.MarshalJSONIndent(keeper.cdc, proposal) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) } - return bz, nil + return res, nil } // Params for query 'custom/gov/deposit' @@ -63,17 +98,17 @@ type QueryDepositParams struct { func queryDeposit(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { var params QueryDepositParams - err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) - if err2 != nil { - return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) + errRes := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if errRes != nil { + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data: %s", errRes.Error())) } deposit, _ := keeper.GetDeposit(ctx, params.ProposalID, params.Depositer) - bz, err2 := wire.MarshalJSONIndent(keeper.cdc, deposit) - if err2 != nil { - panic("could not marshal result to JSON") + res, errRes = wire.MarshalJSONIndent(keeper.cdc, deposit) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) } - return bz, nil + return res, nil } // Params for query 'custom/gov/vote' @@ -84,17 +119,17 @@ type QueryVoteParams struct { func queryVote(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { var params QueryVoteParams - err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) - if err2 != nil { - return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) + errRes := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if errRes != nil { + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data: %s", errRes.Error())) } vote, _ := keeper.GetVote(ctx, params.ProposalID, params.Voter) - bz, err2 := wire.MarshalJSONIndent(keeper.cdc, vote) - if err2 != nil { - panic("could not marshal result to JSON") + res, errRes = wire.MarshalJSONIndent(keeper.cdc, vote) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) } - return bz, nil + return res, nil } // Params for query 'custom/gov/deposits' @@ -104,9 +139,9 @@ type QueryDepositsParams struct { func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { var params QueryDepositParams - err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) - if err2 != nil { - return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) + errRes := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if errRes != nil { + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data: %s", errRes.Error())) } var deposits []Deposit @@ -117,11 +152,11 @@ func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper deposits = append(deposits, deposit) } - bz, err2 := wire.MarshalJSONIndent(keeper.cdc, deposits) - if err2 != nil { - panic("could not marshal result to JSON") + res, errRes = wire.MarshalJSONIndent(keeper.cdc, deposits) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) } - return bz, nil + return res, nil } // Params for query 'custom/gov/votes' @@ -131,10 +166,10 @@ type QueryVotesParams struct { func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { var params QueryVotesParams - err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + errRes := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) - if err2 != nil { - return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) + if errRes != nil { + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data: %s", errRes.Error())) } var votes []Vote @@ -145,35 +180,11 @@ func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke votes = append(votes, vote) } - bz, err2 := wire.MarshalJSONIndent(keeper.cdc, votes) - if err2 != nil { - panic("could not marshal result to JSON") - } - return bz, nil -} - -// Params for query 'custom/gov/proposals' -type QueryProposalsParams struct { - Voter sdk.AccAddress - Depositer sdk.AccAddress - ProposalStatus ProposalStatus - NumLatestProposals int64 -} - -func queryProposals(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { - var params QueryProposalsParams - err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) - if err2 != nil { - return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) - } - - proposals := keeper.GetProposalsFiltered(ctx, params.Voter, params.Depositer, params.ProposalStatus, params.NumLatestProposals) - - bz, err2 := wire.MarshalJSONIndent(keeper.cdc, proposals) - if err2 != nil { - panic("could not marshal result to JSON") + res, errRes = wire.MarshalJSONIndent(keeper.cdc, votes) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) } - return bz, nil + return res, nil } // Params for query 'custom/gov/tally' @@ -185,9 +196,9 @@ func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke // TODO: Dependant on #1914 var proposalID int64 - err2 := keeper.cdc.UnmarshalJSON(req.Data, proposalID) - if err2 != nil { - return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) + errRes := keeper.cdc.UnmarshalJSON(req.Data, proposalID) + if errRes != nil { + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data: %s", errRes.Error())) } proposal := keeper.GetProposal(ctx, proposalID) @@ -205,9 +216,9 @@ func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke _, tallyResult, _ = tally(ctx, keeper, proposal) } - bz, err2 := wire.MarshalJSONIndent(keeper.cdc, tallyResult) - if err2 != nil { - panic("could not marshal result to JSON") + res, errRes = wire.MarshalJSONIndent(keeper.cdc, tallyResult) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) } - return bz, nil + return res, nil } diff --git a/x/stake/app_test.go b/x/stake/app_test.go index bfeff5df760f..df41fee5412b 100644 --- a/x/stake/app_test.go +++ b/x/stake/app_test.go @@ -20,9 +20,9 @@ var ( addr3 = sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) priv4 = ed25519.GenPrivKey() addr4 = sdk.AccAddress(priv4.PubKey().Address()) - coins = sdk.Coins{{"foocoin", sdk.NewInt(10)}} + coins = sdk.Coins{sdk.NewCoin("foocoin", sdk.NewInt(10))} fee = auth.StdFee{ - sdk.Coins{{"foocoin", sdk.NewInt(0)}}, + sdk.Coins{sdk.NewCoin("foocoin", sdk.NewInt(0))}, 100000, } ) diff --git a/x/stake/client/rest/query.go b/x/stake/client/rest/query.go index bf929387dd5d..e011a6b8e322 100644 --- a/x/stake/client/rest/query.go +++ b/x/stake/client/rest/query.go @@ -11,7 +11,6 @@ import ( "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/stake" "github.com/cosmos/cosmos-sdk/x/stake/tags" - "github.com/cosmos/cosmos-sdk/x/stake/types" "github.com/gorilla/mux" ) @@ -50,10 +49,10 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Cod delegationHandlerFn(cliCtx, cdc), ).Methods("GET") - // Query all unbonding_delegations between a delegator and a validator + // Query all unbonding delegations between a delegator and a validator r.HandleFunc( "/stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}", - unbondingDelegationsHandlerFn(cliCtx, cdc), + unbondingDelegationHandlerFn(cliCtx, cdc), ).Methods("GET") // Get all validators @@ -82,96 +81,42 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Cod } -// already resolve the rational shares to not handle this in the client - -// defines a delegation without type Rat for shares -type DelegationWithoutRat struct { - DelegatorAddr sdk.AccAddress `json:"delegator_addr"` - ValidatorAddr sdk.ValAddress `json:"validator_addr"` - Shares string `json:"shares"` - Height int64 `json:"height"` -} - -// aggregation of all delegations, unbondings and redelegations -type DelegationSummary struct { - Delegations []DelegationWithoutRat `json:"delegations"` - UnbondingDelegations []stake.UnbondingDelegation `json:"unbonding_delegations"` - Redelegations []stake.Redelegation `json:"redelegations"` -} - // HTTP request handler to query a delegator delegations func delegatorHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - var valAddr sdk.ValAddress - var delegationSummary = DelegationSummary{} - - // read parameters vars := mux.Vars(r) bech32delegator := vars["delegatorAddr"] - delAddr, err := sdk.AccAddressFromBech32(bech32delegator) + w.Header().Set("Content-Type", "application/json") + + delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } - // Get all validators using key - validators, statusCode, errMsg, err := getBech32Validators(storeName, cliCtx, cdc) - if err != nil { - w.WriteHeader(statusCode) - w.Write([]byte(fmt.Sprintf("%s%s", errMsg, err.Error()))) - return + params := stake.QueryDelegatorParams{ + DelegatorAddr: delegatorAddr, } - for _, validator := range validators { - valAddr = validator.Operator - - // Delegations - delegations, statusCode, errMsg, err := getDelegatorDelegations(cliCtx, cdc, delAddr, valAddr) - if err != nil { - w.WriteHeader(statusCode) - w.Write([]byte(fmt.Sprintf("%s%s", errMsg, err.Error()))) - return - } - if statusCode != http.StatusNoContent { - delegationSummary.Delegations = append(delegationSummary.Delegations, delegations) - } - - // Undelegations - unbondingDelegation, statusCode, errMsg, err := getDelegatorUndelegations(cliCtx, cdc, delAddr, valAddr) - if err != nil { - w.WriteHeader(statusCode) - w.Write([]byte(fmt.Sprintf("%s%s", errMsg, err.Error()))) - return - } - if statusCode != http.StatusNoContent { - delegationSummary.UnbondingDelegations = append(delegationSummary.UnbondingDelegations, unbondingDelegation) - } - - // Redelegations - // only querying redelegations to a validator as this should give us already all relegations - // if we also would put in redelegations from, we would have every redelegation double - redelegations, statusCode, errMsg, err := getDelegatorRedelegations(cliCtx, cdc, delAddr, valAddr) - if err != nil { - w.WriteHeader(statusCode) - w.Write([]byte(fmt.Sprintf("%s%s", errMsg, err.Error()))) - return - } - if statusCode != http.StatusNoContent { - delegationSummary.Redelegations = append(delegationSummary.Redelegations, redelegations) - } + bz, err := cdc.MarshalJSON(params) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return } - output, err := cdc.MarshalJSON(delegationSummary) + res, err := cliCtx.QueryWithData("custom/stake/delegator", bz) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) + return } - w.Write(output) + w.Write(res) } } @@ -184,6 +129,8 @@ func delegatorTxsHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.Hand vars := mux.Vars(r) delegatorAddr := vars["delegatorAddr"] + w.Header().Set("Content-Type", "application/json") + _, err := sdk.AccAddressFromBech32(delegatorAddr) if err != nil { w.WriteHeader(http.StatusBadRequest) @@ -237,7 +184,7 @@ func delegatorTxsHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.Hand foundTxs, errQuery := queryTxs(node, cdc, action, delegatorAddr) if errQuery != nil { w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("error querying transactions. Error: %s", errQuery.Error()))) + w.Write([]byte(errQuery.Error())) } txs = append(txs, foundTxs...) } @@ -253,59 +200,49 @@ func delegatorTxsHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.Hand } // HTTP request handler to query an unbonding-delegation -func unbondingDelegationsHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { +func unbondingDelegationHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) bech32delegator := vars["delegatorAddr"] bech32validator := vars["validatorAddr"] - delAddr, err := sdk.AccAddressFromBech32(bech32delegator) + w.Header().Set("Content-Type", "application/json") + + delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } - valAddr, err := sdk.ValAddressFromBech32(bech32validator) + validatorAddr, err := sdk.ValAddressFromBech32(bech32validator) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } - key := stake.GetUBDKey(delAddr, valAddr) - - res, err := cliCtx.QueryStore(key, storeName) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query unbonding-delegation. Error: %s", err.Error()))) - return - } - - // the query will return empty if there is no data for this record - if len(res) == 0 { - w.WriteHeader(http.StatusNoContent) - return + params := stake.QueryBondsParams{ + DelegatorAddr: delegatorAddr, + ValidatorAddr: validatorAddr, } - ubd, err := types.UnmarshalUBD(cdc, key, res) + bz, err := cdc.MarshalJSON(params) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't unmarshall unbonding-delegation. Error: %s", err.Error()))) + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) return } - // unbondings will be a list in the future but is not yet, but we want to keep the API consistent - ubdArray := []stake.UnbondingDelegation{ubd} - - output, err := cdc.MarshalJSON(ubdArray) + res, err := cliCtx.QueryWithData("custom/stake/unbondingDelegation", bz) if err != nil { w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't marshall unbonding-delegation. Error: %s", err.Error()))) + w.Write([]byte(err.Error())) + return } - w.Write(output) + w.Write(res) } } @@ -317,71 +254,55 @@ func delegationHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.Handle bech32delegator := vars["delegatorAddr"] bech32validator := vars["validatorAddr"] - delAddr, err := sdk.AccAddressFromBech32(bech32delegator) + w.Header().Set("Content-Type", "application/json") + + delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } - valAddr, err := sdk.ValAddressFromBech32(bech32validator) + validatorAddr, err := sdk.ValAddressFromBech32(bech32validator) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } - key := stake.GetDelegationKey(delAddr, valAddr) - - res, err := cliCtx.QueryStore(key, storeName) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query delegation. Error: %s", err.Error()))) - return + params := stake.QueryBondsParams{ + DelegatorAddr: delegatorAddr, + ValidatorAddr: validatorAddr, } - // the query will return empty if there is no data for this record - if len(res) == 0 { - w.WriteHeader(http.StatusNoContent) - return - } - - delegation, err := types.UnmarshalDelegation(cdc, key, res) + bz, err := cdc.MarshalJSON(params) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } - outputDelegation := DelegationWithoutRat{ - DelegatorAddr: delegation.DelegatorAddr, - ValidatorAddr: delegation.ValidatorAddr, - Height: delegation.Height, - Shares: delegation.Shares.String(), - } - - output, err := cdc.MarshalJSON(outputDelegation) + res, err := cliCtx.QueryWithData("custom/stake/delegation", bz) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) + return } - w.Write(output) + w.Write(res) } } // HTTP request handler to query all delegator bonded validators func delegatorValidatorsHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - - var valAddr sdk.ValAddress - var bondedValidators []types.BechValidator - // read parameters vars := mux.Vars(r) bech32delegator := vars["delegatorAddr"] + w.Header().Set("Content-Type", "application/json") + delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) if err != nil { w.WriteHeader(http.StatusBadRequest) @@ -389,120 +310,87 @@ func delegatorValidatorsHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) ht return } - // Get all validators using key - kvs, err := cliCtx.QuerySubspace(stake.ValidatorsKey, storeName) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query validators. Error: %s", err.Error()))) - return - } else if len(kvs) == 0 { - // the query will return empty if there are no validators - w.WriteHeader(http.StatusNoContent) - return + params := stake.QueryDelegatorParams{ + DelegatorAddr: delegatorAddr, } - validators, err := getValidators(kvs, cdc) + bz, err := cdc.MarshalJSON(params) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Error: %s", err.Error()))) + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) return } - for _, validator := range validators { - // get all transactions from the delegator to val and append - valAddr = validator.Operator - - validator, statusCode, errMsg, errRes := getDelegatorValidator(cliCtx, cdc, delegatorAddr, valAddr) - if errRes != nil { - w.WriteHeader(statusCode) - w.Write([]byte(fmt.Sprintf("%s%s", errMsg, errRes.Error()))) - return - } else if statusCode == http.StatusNoContent { - continue - } - - bondedValidators = append(bondedValidators, validator) - } - output, err := cdc.MarshalJSON(bondedValidators) + res, err := cliCtx.QueryWithData("custom/stake/delegatorValidators", bz) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) + return } - w.Write(output) + + w.Write(res) } } // HTTP request handler to get information from a currently bonded validator func delegatorValidatorHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - // read parameters - var output []byte + vars := mux.Vars(r) bech32delegator := vars["delegatorAddr"] bech32validator := vars["validatorAddr"] - delAddr, err := sdk.AccAddressFromBech32(bech32delegator) - valAddr, err := sdk.ValAddressFromBech32(bech32validator) + w.Header().Set("Content-Type", "application/json") + + delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) + validatorAddr, err := sdk.ValAddressFromBech32(bech32validator) if err != nil { w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(fmt.Sprintf("Error: %s", err.Error()))) + w.Write([]byte(err.Error())) return } - // Check if there if the delegator is bonded or redelegated to the validator + params := stake.QueryBondsParams{ + DelegatorAddr: delegatorAddr, + ValidatorAddr: validatorAddr, + } - validator, statusCode, errMsg, err := getDelegatorValidator(cliCtx, cdc, delAddr, valAddr) + bz, err := cdc.MarshalJSON(params) if err != nil { - w.WriteHeader(statusCode) - w.Write([]byte(fmt.Sprintf("%s%s", errMsg, err.Error()))) - return - } else if statusCode == http.StatusNoContent { - w.WriteHeader(statusCode) + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) return } - output, err = cdc.MarshalJSON(validator) + + res, err := cliCtx.QueryWithData("custom/stake/delegatorValidator", bz) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) + return } - w.Write(output) + + w.Write(res) } } -// TODO bech32 -// http request handler to query list of validators +// HTTP request handler to query list of validators func validatorsHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - kvs, err := cliCtx.QuerySubspace(stake.ValidatorsKey, storeName) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query validators. Error: %s", err.Error()))) - return - } - // the query will return empty if there are no validators - if len(kvs) == 0 { - w.WriteHeader(http.StatusNoContent) - return - } - - validators, err := getValidators(kvs, cdc) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Error: %s", err.Error()))) - return - } + w.Header().Set("Content-Type", "application/json") - output, err := cdc.MarshalJSON(validators) + res, err := cliCtx.QueryWithData("custom/stake/validators", nil) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) + return } - w.Write(output) + w.Header().Set("Content-Type", "application/json") + w.Write(res) } } @@ -510,118 +398,73 @@ func validatorsHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.Handle func validatorHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - var output []byte - // read parameters vars := mux.Vars(r) bech32validatorAddr := vars["addr"] - valAddr, err := sdk.ValAddressFromBech32(bech32validatorAddr) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(fmt.Sprintf("error: %s", err.Error()))) - return - } + w.Header().Set("Content-Type", "application/json") - key := stake.GetValidatorKey(valAddr) - - res, err := cliCtx.QueryStore(key, storeName) + validatorAddr, err := sdk.ValAddressFromBech32(bech32validatorAddr) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query validator, error: %s", err.Error()))) + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) return } - // the query will return empty if there is no data for this record - if len(res) == 0 { - w.WriteHeader(http.StatusNoContent) - return + params := stake.QueryValidatorParams{ + ValidatorAddr: validatorAddr, } - validator, err := types.UnmarshalValidator(cdc, valAddr, res) + bz, err := cdc.MarshalJSON(params) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } - bech32Validator, err := validator.Bech32Validator() + res, err := cliCtx.QueryWithData("custom/stake/validator", bz) if err != nil { - w.WriteHeader(http.StatusBadRequest) + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) - return - } - output, err = cdc.MarshalJSON(bech32Validator) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("Error: %s", err.Error()))) return } - if output == nil { - w.WriteHeader(http.StatusNoContent) - return - } - w.Write(output) + w.Write(res) } } // HTTP request handler to query the pool information func poolHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - key := stake.PoolKey - res, err := cliCtx.QueryStore(key, storeName) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query pool. Error: %s", err.Error()))) - return - } + w.Header().Set("Content-Type", "application/json") - pool, err := types.UnmarshalPool(cdc, res) + res, err := cliCtx.QueryWithData("custom/stake/pool", nil) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) - return - } - output, err := cdc.MarshalJSON(pool) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) return } - w.Write(output) + w.Write(res) } } // HTTP request handler to query the staking params values func paramsHandlerFn(cliCtx context.CLIContext, cdc *wire.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - key := stake.ParamKey - res, err := cliCtx.QueryStore(key, storeName) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(fmt.Sprintf("couldn't query parameters. Error: %s", err.Error()))) - return - } + w.Header().Set("Content-Type", "application/json") - params, err := types.UnmarshalParams(cdc, res) + res, err := cliCtx.QueryWithData("custom/stake/parameters", nil) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) - return - } - output, err := cdc.MarshalJSON(params) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) return } - w.Write(output) + w.Write(res) } } diff --git a/x/stake/client/rest/utils.go b/x/stake/client/rest/utils.go index 171ded2b35f1..8013c256421e 100644 --- a/x/stake/client/rest/utils.go +++ b/x/stake/client/rest/utils.go @@ -2,15 +2,10 @@ package rest import ( "fmt" - "net/http" - "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/tx" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" - "github.com/cosmos/cosmos-sdk/x/stake" "github.com/cosmos/cosmos-sdk/x/stake/tags" - "github.com/cosmos/cosmos-sdk/x/stake/types" rpcclient "github.com/tendermint/tendermint/rpc/client" ) @@ -24,110 +19,6 @@ func contains(stringSlice []string, txType string) bool { return false } -func getDelegatorValidator(cliCtx context.CLIContext, cdc *wire.Codec, delAddr sdk.AccAddress, valAddr sdk.ValAddress) ( - bech32Validator types.BechValidator, httpStatusCode int, errMsg string, err error) { - - key := stake.GetDelegationKey(delAddr, valAddr) - res, err := cliCtx.QueryStore(key, storeName) - if err != nil { - return types.BechValidator{}, http.StatusInternalServerError, "couldn't query delegation. Error: ", err - } - if len(res) == 0 { - return types.BechValidator{}, http.StatusNoContent, "", nil - } - - key = stake.GetValidatorKey(valAddr) - res, err = cliCtx.QueryStore(key, storeName) - if err != nil { - return types.BechValidator{}, http.StatusInternalServerError, "couldn't query validator. Error: ", err - } - if len(res) == 0 { - return types.BechValidator{}, http.StatusNoContent, "", nil - } - validator, err := types.UnmarshalValidator(cdc, valAddr, res) - if err != nil { - return types.BechValidator{}, http.StatusBadRequest, "", err - } - bech32Validator, err = validator.Bech32Validator() - if err != nil { - return types.BechValidator{}, http.StatusBadRequest, "", err - } - - return bech32Validator, http.StatusOK, "", nil -} - -func getDelegatorDelegations( - cliCtx context.CLIContext, cdc *wire.Codec, delAddr sdk.AccAddress, valAddr sdk.ValAddress) ( - outputDelegation DelegationWithoutRat, httpStatusCode int, errMsg string, err error) { - - delegationKey := stake.GetDelegationKey(delAddr, valAddr) - marshalledDelegation, err := cliCtx.QueryStore(delegationKey, storeName) - if err != nil { - return DelegationWithoutRat{}, http.StatusInternalServerError, "couldn't query delegation. Error: ", err - } - - if len(marshalledDelegation) == 0 { - return DelegationWithoutRat{}, http.StatusNoContent, "", nil - } - - delegation, err := types.UnmarshalDelegation(cdc, delegationKey, marshalledDelegation) - if err != nil { - return DelegationWithoutRat{}, http.StatusInternalServerError, "couldn't unmarshall delegation. Error: ", err - } - - outputDelegation = DelegationWithoutRat{ - DelegatorAddr: delegation.DelegatorAddr, - ValidatorAddr: delegation.ValidatorAddr, - Height: delegation.Height, - Shares: delegation.Shares.String(), - } - - return outputDelegation, http.StatusOK, "", nil -} - -func getDelegatorUndelegations( - cliCtx context.CLIContext, cdc *wire.Codec, delAddr sdk.AccAddress, valAddr sdk.ValAddress) ( - unbonds types.UnbondingDelegation, httpStatusCode int, errMsg string, err error) { - - undelegationKey := stake.GetUBDKey(delAddr, valAddr) - marshalledUnbondingDelegation, err := cliCtx.QueryStore(undelegationKey, storeName) - if err != nil { - return types.UnbondingDelegation{}, http.StatusInternalServerError, "couldn't query unbonding-delegation. Error: ", err - } - - if len(marshalledUnbondingDelegation) == 0 { - return types.UnbondingDelegation{}, http.StatusNoContent, "", nil - } - - unbondingDelegation, err := types.UnmarshalUBD(cdc, undelegationKey, marshalledUnbondingDelegation) - if err != nil { - return types.UnbondingDelegation{}, http.StatusInternalServerError, "couldn't unmarshall unbonding-delegation. Error: ", err - } - return unbondingDelegation, http.StatusOK, "", nil -} - -func getDelegatorRedelegations( - cliCtx context.CLIContext, cdc *wire.Codec, delAddr sdk.AccAddress, valAddr sdk.ValAddress) ( - regelegations types.Redelegation, httpStatusCode int, errMsg string, err error) { - - key := stake.GetREDsByDelToValDstIndexKey(delAddr, valAddr) - marshalledRedelegations, err := cliCtx.QueryStore(key, storeName) - if err != nil { - return types.Redelegation{}, http.StatusInternalServerError, "couldn't query redelegation. Error: ", err - } - - if len(marshalledRedelegations) == 0 { - return types.Redelegation{}, http.StatusNoContent, "", nil - } - - redelegations, err := types.UnmarshalRED(cdc, key, marshalledRedelegations) - if err != nil { - return types.Redelegation{}, http.StatusInternalServerError, "couldn't unmarshall redelegations. Error: ", err - } - - return redelegations, http.StatusOK, "", nil -} - // queries staking txs func queryTxs(node rpcclient.Client, cdc *wire.Codec, tag string, delegatorAddr string) ([]tx.Info, error) { page := 0 @@ -141,45 +32,3 @@ func queryTxs(node rpcclient.Client, cdc *wire.Codec, tag string, delegatorAddr return tx.FormatTxResults(cdc, res.Txs) } - -// gets all validators -func getValidators(validatorKVs []sdk.KVPair, cdc *wire.Codec) ([]types.BechValidator, error) { - validators := make([]types.BechValidator, len(validatorKVs)) - for i, kv := range validatorKVs { - - addr := kv.Key[1:] - validator, err := types.UnmarshalValidator(cdc, addr, kv.Value) - if err != nil { - return nil, err - } - - bech32Validator, err := validator.Bech32Validator() - if err != nil { - return nil, err - } - validators[i] = bech32Validator - } - return validators, nil -} - -// gets all Bech32 validators from a key -func getBech32Validators(storeName string, cliCtx context.CLIContext, cdc *wire.Codec) ( - validators []types.BechValidator, httpStatusCode int, errMsg string, err error) { - - // Get all validators using key - kvs, err := cliCtx.QuerySubspace(stake.ValidatorsKey, storeName) - if err != nil { - return nil, http.StatusInternalServerError, "couldn't query validators. Error: ", err - } - - // the query will return empty if there are no validators - if len(kvs) == 0 { - return nil, http.StatusNoContent, "", nil - } - - validators, err = getValidators(kvs, cdc) - if err != nil { - return nil, http.StatusInternalServerError, "Error: ", err - } - return validators, http.StatusOK, "", nil -} diff --git a/x/stake/genesis.go b/x/stake/genesis.go index 7a004bccd270..06a90a9e6246 100644 --- a/x/stake/genesis.go +++ b/x/stake/genesis.go @@ -60,7 +60,7 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [ func WriteGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { pool := keeper.GetPool(ctx) params := keeper.GetParams(ctx) - validators := keeper.GetAllValidators(ctx) + validators := keeper.GetValidators(ctx) bonds := keeper.GetAllDelegations(ctx) return types.GenesisState{ diff --git a/x/stake/genesis_test.go b/x/stake/genesis_test.go index ddd29f6f8498..b662d24cea48 100644 --- a/x/stake/genesis_test.go +++ b/x/stake/genesis_test.go @@ -42,6 +42,12 @@ func TestInitGenesis(t *testing.T) { vals, err := InitGenesis(ctx, keeper, genesisState) require.NoError(t, err) + actualGenesis := WriteGenesis(ctx, keeper) + require.Equal(t, genesisState.Pool, actualGenesis.Pool) + require.Equal(t, genesisState.Params, actualGenesis.Params) + require.Equal(t, genesisState.Bonds, actualGenesis.Bonds) + require.EqualValues(t, keeper.GetValidators(ctx), actualGenesis.Validators) + // now make sure the validators are bonded and intra-tx counters are correct resVal, found := keeper.GetValidator(ctx, sdk.ValAddress(keep.Addrs[0])) require.True(t, found) diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index 68c342fd8793..10f55742896b 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -17,14 +17,14 @@ import ( //______________________________________________________________________ func newTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt int64) MsgCreateValidator { - return types.NewMsgCreateValidator(address, pubKey, sdk.Coin{"steak", sdk.NewInt(amt)}, Description{}) + return types.NewMsgCreateValidator(address, pubKey, sdk.NewCoin("steak", sdk.NewInt(amt)), Description{}) } func newTestMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amt int64) MsgDelegate { return MsgDelegate{ DelegatorAddr: delAddr, ValidatorAddr: valAddr, - Delegation: sdk.Coin{"steak", sdk.NewInt(amt)}, + Delegation: sdk.NewCoin("steak", sdk.NewInt(amt)), } } @@ -34,7 +34,7 @@ func newTestMsgCreateValidatorOnBehalfOf(delAddr sdk.AccAddress, valAddr sdk.Val DelegatorAddr: delAddr, ValidatorAddr: valAddr, PubKey: valPubKey, - Delegation: sdk.Coin{"steak", sdk.NewInt(amt)}, + Delegation: sdk.NewCoin("steak", sdk.NewInt(amt)), } } diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index 9a596ffbce5e..70fd3beef118 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -8,7 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/stake/types" ) -// load a delegation +// return a specific delegation func (k Keeper) GetDelegation(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) ( delegation types.Delegation, found bool) { @@ -24,41 +24,39 @@ func (k Keeper) GetDelegation(ctx sdk.Context, return delegation, true } -// load all delegations used during genesis dump +// return all delegations used during genesis dump func (k Keeper) GetAllDelegations(ctx sdk.Context) (delegations []types.Delegation) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, DelegationKey) - i := 0 - for ; ; i++ { - if !iterator.Valid() { - break - } + for ; iterator.Valid(); iterator.Next() { delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Key(), iterator.Value()) delegations = append(delegations, delegation) - iterator.Next() } iterator.Close() return delegations } -// load all delegations for a delegator -func (k Keeper) GetDelegations(ctx sdk.Context, delegator sdk.AccAddress, - maxRetrieve int16) (delegations []types.Delegation) { - +// Return all delegations for a delegator. If maxRetrieve is supplied, the respective amount will be returned. +func (k Keeper) GetDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAddress, + maxRetrieve ...int16) (delegations []types.Delegation) { + retrieve := len(maxRetrieve) > 0 + if retrieve { + delegations = make([]types.Delegation, maxRetrieve[0]) + } store := ctx.KVStore(k.storeKey) delegatorPrefixKey := GetDelegationsKey(delegator) iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest - delegations = make([]types.Delegation, maxRetrieve) i := 0 - for ; ; i++ { - if !iterator.Valid() || i > int(maxRetrieve-1) { - break - } + for ; iterator.Valid() && (!retrieve || (retrieve && i < int(maxRetrieve[0]))); iterator.Next() { delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Key(), iterator.Value()) - delegations[i] = delegation - iterator.Next() + if retrieve { + delegations[i] = delegation + } else { + delegations = append(delegations, delegation) + } + i++ } iterator.Close() return delegations[:i] // trim @@ -71,7 +69,7 @@ func (k Keeper) SetDelegation(ctx sdk.Context, delegation types.Delegation) { store.Set(GetDelegationKey(delegation.DelegatorAddr, delegation.ValidatorAddr), b) } -// remove the delegation +// remove a delegation from store func (k Keeper) RemoveDelegation(ctx sdk.Context, delegation types.Delegation) { store := ctx.KVStore(k.storeKey) store.Delete(GetDelegationKey(delegation.DelegatorAddr, delegation.ValidatorAddr)) @@ -79,7 +77,33 @@ func (k Keeper) RemoveDelegation(ctx sdk.Context, delegation types.Delegation) { //_____________________________________________________________________________________ -// load a unbonding delegation +// Return all unbonding delegations for a delegator. If maxRetrieve is supplied, the respective amount will be returned. +func (k Keeper) GetUnbondingDelegations(ctx sdk.Context, delegator sdk.AccAddress, + maxRetrieve ...int16) (unbondingDelegations []types.UnbondingDelegation) { + + retrieve := len(maxRetrieve) > 0 + if retrieve { + unbondingDelegations = make([]types.UnbondingDelegation, maxRetrieve[0]) + } + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := GetUBDsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest + + i := 0 + for ; iterator.Valid() && (!retrieve || (retrieve && i < int(maxRetrieve[0]))); iterator.Next() { + unbondingDelegation := types.MustUnmarshalUBD(k.cdc, iterator.Key(), iterator.Value()) + if retrieve { + unbondingDelegations[i] = unbondingDelegation + } else { + unbondingDelegations = append(unbondingDelegations, unbondingDelegation) + } + i++ + } + iterator.Close() + return unbondingDelegations[:i] // trim +} + +// return a unbonding delegation func (k Keeper) GetUnbondingDelegation(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (ubd types.UnbondingDelegation, found bool) { @@ -94,20 +118,18 @@ func (k Keeper) GetUnbondingDelegation(ctx sdk.Context, return ubd, true } -// load all unbonding delegations from a particular validator +// return all unbonding delegations from a particular validator func (k Keeper) GetUnbondingDelegationsFromValidator(ctx sdk.Context, valAddr sdk.ValAddress) (ubds []types.UnbondingDelegation) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, GetUBDsByValIndexKey(valAddr)) - for { - if !iterator.Valid() { - break - } + + for ; iterator.Valid(); iterator.Next() { key := GetUBDKeyFromValIndexKey(iterator.Key()) value := store.Get(key) ubd := types.MustUnmarshalUBD(k.cdc, key, value) ubds = append(ubds, ubd) - iterator.Next() } + iterator.Close() return ubds } @@ -116,8 +138,8 @@ func (k Keeper) GetUnbondingDelegationsFromValidator(ctx sdk.Context, valAddr sd func (k Keeper) IterateUnbondingDelegations(ctx sdk.Context, fn func(index int64, ubd types.UnbondingDelegation) (stop bool)) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, UnbondingDelegationKey) - i := int64(0) - for ; iterator.Valid(); iterator.Next() { + + for i := int64(0); iterator.Valid(); iterator.Next() { ubd := types.MustUnmarshalUBD(k.cdc, iterator.Key(), iterator.Value()) stop := fn(i, ubd) if stop { @@ -147,7 +169,33 @@ func (k Keeper) RemoveUnbondingDelegation(ctx sdk.Context, ubd types.UnbondingDe //_____________________________________________________________________________________ -// load a redelegation +// Return all redelegations for a delegator. If maxRetrieve is supplied, the respective amount will be returned. +func (k Keeper) GetRedelegations(ctx sdk.Context, delegator sdk.AccAddress, + maxRetrieve ...int16) (redelegations []types.Redelegation) { + + retrieve := len(maxRetrieve) > 0 + if retrieve { + redelegations = make([]types.Redelegation, maxRetrieve[0]) + } + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := GetREDsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest + + i := 0 + for ; iterator.Valid() && (!retrieve || (retrieve && i < int(maxRetrieve[0]))); iterator.Next() { + redelegation := types.MustUnmarshalRED(k.cdc, iterator.Key(), iterator.Value()) + if retrieve { + redelegations[i] = redelegation + } else { + redelegations = append(redelegations, redelegation) + } + i++ + } + iterator.Close() + return redelegations[:i] // trim +} + +// return a redelegation func (k Keeper) GetRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) (red types.Redelegation, found bool) { @@ -162,25 +210,21 @@ func (k Keeper) GetRedelegation(ctx sdk.Context, return red, true } -// load all redelegations from a particular validator +// return all redelegations from a particular validator func (k Keeper) GetRedelegationsFromValidator(ctx sdk.Context, valAddr sdk.ValAddress) (reds []types.Redelegation) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, GetREDsFromValSrcIndexKey(valAddr)) - for { - if !iterator.Valid() { - break - } + for ; iterator.Valid(); iterator.Next() { key := GetREDKeyFromValSrcIndexKey(iterator.Key()) value := store.Get(key) red := types.MustUnmarshalRED(k.cdc, key, value) reds = append(reds, red) - iterator.Next() } iterator.Close() return reds } -// has a redelegation +// check if validator is receiving a redelegation func (k Keeper) HasReceivingRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, valDstAddr sdk.ValAddress) bool { @@ -358,7 +402,7 @@ func (k Keeper) BeginUnbonding(ctx sdk.Context, // create the unbonding delegation params := k.GetParams(ctx) minTime, height, completeNow := k.getBeginInfo(ctx, params, valAddr) - balance := sdk.Coin{params.BondDenom, returnAmount.RoundInt()} + balance := sdk.NewCoin(params.BondDenom, returnAmount.RoundInt()) // no need to create the ubd object just complete now if completeNow { @@ -418,7 +462,7 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, } params := k.GetParams(ctx) - returnCoin := sdk.Coin{params.BondDenom, returnAmount.RoundInt()} + returnCoin := sdk.NewCoin(params.BondDenom, returnAmount.RoundInt()) dstValidator, found := k.GetValidator(ctx, valDstAddr) if !found { return types.ErrBadRedelegationDst(k.Codespace()) diff --git a/x/stake/keeper/delegation_test.go b/x/stake/keeper/delegation_test.go index 023642bb227d..762e828110e5 100644 --- a/x/stake/keeper/delegation_test.go +++ b/x/stake/keeper/delegation_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/require" ) -// tests GetDelegation, GetDelegations, SetDelegation, RemoveDelegation, GetDelegations +// tests GetDelegation, GetDelegatorDelegations, SetDelegation, RemoveDelegation, GetDelegatorDelegations func TestDelegation(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 10) pool := keeper.GetPool(ctx) @@ -30,6 +30,7 @@ func TestDelegation(t *testing.T) { validators[2] = keeper.UpdateValidator(ctx, validators[2]) // first add a validators[0] to delegate too + bond1to1 := types.Delegation{ DelegatorAddr: addrDels[0], ValidatorAddr: addrVals[0], @@ -66,16 +67,16 @@ func TestDelegation(t *testing.T) { keeper.SetDelegation(ctx, bond2to3) // test all bond retrieve capabilities - resBonds := keeper.GetDelegations(ctx, addrDels[0], 5) + resBonds := keeper.GetDelegatorDelegations(ctx, addrDels[0], 5) require.Equal(t, 3, len(resBonds)) require.True(t, bond1to1.Equal(resBonds[0])) require.True(t, bond1to2.Equal(resBonds[1])) require.True(t, bond1to3.Equal(resBonds[2])) - resBonds = keeper.GetDelegations(ctx, addrDels[0], 3) + resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[0], 3) require.Equal(t, 3, len(resBonds)) - resBonds = keeper.GetDelegations(ctx, addrDels[0], 2) + resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[0], 2) require.Equal(t, 2, len(resBonds)) - resBonds = keeper.GetDelegations(ctx, addrDels[1], 5) + resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[1], 5) require.Equal(t, 3, len(resBonds)) require.True(t, bond2to1.Equal(resBonds[0])) require.True(t, bond2to2.Equal(resBonds[1])) @@ -89,15 +90,43 @@ func TestDelegation(t *testing.T) { require.True(t, bond2to2.Equal(allBonds[4])) require.True(t, bond2to3.Equal(allBonds[5])) + resVals := keeper.GetDelegatorValidators(ctx, addrDels[0]) + require.Equal(t, 3, len(resVals)) + resVals = keeper.GetDelegatorValidators(ctx, addrDels[1], 4) + require.Equal(t, 3, len(resVals)) + + resBechVals := keeper.GetDelegatorBechValidators(ctx, addrDels[0]) + require.Equal(t, 3, len(resBechVals)) + + for i := 0; i < 3; i++ { + + resVal := keeper.GetDelegatorValidator(ctx, addrDels[0], addrVals[i]) + require.Equal(t, addrVals[i], resVal.GetOperator()) + + resBechVal, err := resVal.Bech32Validator() + require.Nil(t, err) + require.Equal(t, addrVals[i].String(), resBechVal.Operator) + + resVal = keeper.GetDelegatorValidator(ctx, addrDels[1], addrVals[i]) + require.Equal(t, addrVals[i], resVal.GetOperator()) + + resBechVal, err = resVal.Bech32Validator() + require.Nil(t, err) + require.Equal(t, addrVals[i].String(), resBechVal.Operator) + } + // delete a record keeper.RemoveDelegation(ctx, bond2to3) _, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[2]) require.False(t, found) - resBonds = keeper.GetDelegations(ctx, addrDels[1], 5) + resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[1], 5) require.Equal(t, 2, len(resBonds)) require.True(t, bond2to1.Equal(resBonds[0])) require.True(t, bond2to2.Equal(resBonds[1])) + resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[1]) + require.Equal(t, 2, len(resBonds)) + // delete all the records from delegator 2 keeper.RemoveDelegation(ctx, bond2to1) keeper.RemoveDelegation(ctx, bond2to2) @@ -105,8 +134,11 @@ func TestDelegation(t *testing.T) { require.False(t, found) _, found = keeper.GetDelegation(ctx, addrDels[1], addrVals[1]) require.False(t, found) - resBonds = keeper.GetDelegations(ctx, addrDels[1], 5) + resBonds = keeper.GetDelegatorDelegations(ctx, addrDels[1], 5) require.Equal(t, 0, len(resBonds)) + + // resBondsNoRat = keeper.GetDelegatorDelegationsWithoutRat(ctx, addrDels[1], 5) + // require.Equal(t, 0, len(resBondsNoRat)) } // tests Get/Set/Remove UnbondingDelegation @@ -123,21 +155,32 @@ func TestUnbondingDelegation(t *testing.T) { // set and retrieve a record keeper.SetUnbondingDelegation(ctx, ubd) - resBond, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + resUnbond, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) require.True(t, found) - require.True(t, ubd.Equal(resBond)) + require.True(t, ubd.Equal(resUnbond)) // modify a records, save, and retrieve ubd.Balance = sdk.NewInt64Coin("steak", 21) keeper.SetUnbondingDelegation(ctx, ubd) - resBond, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) + + resUnbonds := keeper.GetUnbondingDelegations(ctx, addrDels[0], 5) + require.Equal(t, 1, len(resUnbonds)) + + resUnbonds = keeper.GetUnbondingDelegations(ctx, addrDels[0]) + require.Equal(t, 1, len(resUnbonds)) + + resUnbond, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) require.True(t, found) - require.True(t, ubd.Equal(resBond)) + require.True(t, ubd.Equal(resUnbond)) // delete a record keeper.RemoveUnbondingDelegation(ctx, ubd) _, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) require.False(t, found) + + resUnbonds = keeper.GetUnbondingDelegations(ctx, addrDels[0], 5) + require.Equal(t, 0, len(resUnbonds)) + } func TestUnbondDelegation(t *testing.T) { @@ -413,12 +456,20 @@ func TestRedelegation(t *testing.T) { // set and retrieve a record keeper.SetRedelegation(ctx, rd) - resBond, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + resRed, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) require.True(t, found) redelegations := keeper.GetRedelegationsFromValidator(ctx, addrVals[0]) require.Equal(t, 1, len(redelegations)) - require.True(t, redelegations[0].Equal(resBond)) + require.True(t, redelegations[0].Equal(resRed)) + + redelegations = keeper.GetRedelegations(ctx, addrDels[0], 5) + require.Equal(t, 1, len(redelegations)) + require.True(t, redelegations[0].Equal(resRed)) + + redelegations = keeper.GetRedelegations(ctx, addrDels[0]) + require.Equal(t, 1, len(redelegations)) + require.True(t, redelegations[0].Equal(resRed)) // check if has the redelegation has = keeper.HasReceivingRedelegation(ctx, addrDels[0], addrVals[1]) @@ -429,18 +480,25 @@ func TestRedelegation(t *testing.T) { rd.SharesDst = sdk.NewDec(21) keeper.SetRedelegation(ctx, rd) - resBond, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) + resRed, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) require.True(t, found) - require.True(t, rd.Equal(resBond)) + require.True(t, rd.Equal(resRed)) redelegations = keeper.GetRedelegationsFromValidator(ctx, addrVals[0]) require.Equal(t, 1, len(redelegations)) - require.True(t, redelegations[0].Equal(resBond)) + require.True(t, redelegations[0].Equal(resRed)) + + redelegations = keeper.GetRedelegations(ctx, addrDels[0], 5) + require.Equal(t, 1, len(redelegations)) + require.True(t, redelegations[0].Equal(resRed)) // delete a record keeper.RemoveRedelegation(ctx, rd) _, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) require.False(t, found) + + redelegations = keeper.GetRedelegations(ctx, addrDels[0], 5) + require.Equal(t, 0, len(redelegations)) } func TestRedelegateSelfDelegation(t *testing.T) { diff --git a/x/stake/keeper/query_utils.go b/x/stake/keeper/query_utils.go new file mode 100644 index 000000000000..023f8ec8f294 --- /dev/null +++ b/x/stake/keeper/query_utils.go @@ -0,0 +1,173 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/stake/types" +) + +// Return all validators that a delegator is bonded to. If maxRetrieve is supplied, the respective amount will be returned. +func (k Keeper) GetDelegatorValidators(ctx sdk.Context, delegatorAddr sdk.AccAddress, + maxRetrieve ...int16) (validators []types.Validator) { + + retrieve := len(maxRetrieve) > 0 + if retrieve { + validators = make([]types.Validator, maxRetrieve[0]) + } + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := GetDelegationsKey(delegatorAddr) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest + + i := 0 + for ; iterator.Valid() && (!retrieve || (retrieve && i < int(maxRetrieve[0]))); iterator.Next() { + addr := iterator.Key() + delegation := types.MustUnmarshalDelegation(k.cdc, addr, iterator.Value()) + validator, found := k.GetValidator(ctx, delegation.ValidatorAddr) + if !found { + panic(types.ErrNoValidatorFound(types.DefaultCodespace)) + } + + if retrieve { + validators[i] = validator + } else { + validators = append(validators, validator) + } + + i++ + } + iterator.Close() + return validators[:i] // trim +} + +// Return all validators that a delegator is bonded to. If maxRetrieve is supplied, the respective amount will be returned. +func (k Keeper) GetDelegatorBechValidators(ctx sdk.Context, delegatorAddr sdk.AccAddress, + maxRetrieve ...int16) (validators []types.BechValidator) { + + retrieve := len(maxRetrieve) > 0 + if retrieve { + validators = make([]types.BechValidator, maxRetrieve[0]) + } + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := GetDelegationsKey(delegatorAddr) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest + + i := 0 + for ; iterator.Valid() && (!retrieve || (retrieve && i < int(maxRetrieve[0]))); iterator.Next() { + addr := iterator.Key() + delegation := types.MustUnmarshalDelegation(k.cdc, addr, iterator.Value()) + validator, found := k.GetValidator(ctx, delegation.ValidatorAddr) + if !found { + panic(types.ErrNoValidatorFound(types.DefaultCodespace)) + } + + bechValidator, err := validator.Bech32Validator() + if err != nil { + panic(err.Error()) + } + + if retrieve { + validators[i] = bechValidator + } else { + validators = append(validators, bechValidator) + } + i++ + } + iterator.Close() + return validators[:i] // trim +} + +// return a validator that a delegator is bonded to +func (k Keeper) GetDelegatorValidator(ctx sdk.Context, delegatorAddr sdk.AccAddress, + validatorAddr sdk.ValAddress) (validator types.Validator) { + + delegation, found := k.GetDelegation(ctx, delegatorAddr, validatorAddr) + if !found { + panic(types.ErrNoDelegation(types.DefaultCodespace)) + } + + validator, found = k.GetValidator(ctx, delegation.ValidatorAddr) + if !found { + panic(types.ErrNoValidatorFound(types.DefaultCodespace)) + } + return +} + +// Return all delegations for a delegator. If maxRetrieve is supplied, the respective amount will be returned. +func (k Keeper) GetDelegatorDelegationsREST(ctx sdk.Context, delegator sdk.AccAddress, + maxRetrieve ...int16) (delegations []types.DelegationREST) { + retrieve := len(maxRetrieve) > 0 + if retrieve { + delegations = make([]types.DelegationREST, maxRetrieve[0]) + } + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := GetDelegationsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest + + i := 0 + for ; iterator.Valid() && (!retrieve || (retrieve && i < int(maxRetrieve[0]))); iterator.Next() { + delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Key(), iterator.Value()) + if retrieve { + delegations[i] = delegation.ToRest() + } else { + delegations = append(delegations, delegation.ToRest()) + } + i++ + } + iterator.Close() + return delegations[:i] // trim +} + +// Return all redelegations for a delegator. If maxRetrieve is supplied, the respective amount will be returned. +func (k Keeper) GetRedelegationsREST(ctx sdk.Context, delegator sdk.AccAddress, + maxRetrieve ...int16) (redelegations []types.RedelegationREST) { + + retrieve := len(maxRetrieve) > 0 + if retrieve { + redelegations = make([]types.RedelegationREST, maxRetrieve[0]) + } + store := ctx.KVStore(k.storeKey) + delegatorPrefixKey := GetREDsKey(delegator) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest + + i := 0 + for ; iterator.Valid() && (!retrieve || (retrieve && i < int(maxRetrieve[0]))); iterator.Next() { + redelegation := types.MustUnmarshalRED(k.cdc, iterator.Key(), iterator.Value()) + if retrieve { + redelegations[i] = redelegation.ToRest() + } else { + redelegations = append(redelegations, redelegation.ToRest()) + } + i++ + } + iterator.Close() + return redelegations[:i] // trim +} + +// Get the set of all validators. If maxRetrieve is supplied, the respective amount will be returned. +func (k Keeper) GetBechValidators(ctx sdk.Context, maxRetrieve ...int16) (validators []types.BechValidator) { + retrieve := len(maxRetrieve) > 0 + if retrieve { + validators = make([]types.BechValidator, maxRetrieve[0]) + } + + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey) + + i := 0 + for ; iterator.Valid() && (!retrieve || (retrieve && i < int(maxRetrieve[0]))); iterator.Next() { + addr := iterator.Key()[1:] + validator := types.MustUnmarshalValidator(k.cdc, addr, iterator.Value()) + bechValidator, err := validator.Bech32Validator() + if err != nil { + panic(err.Error()) + } + + if retrieve { + validators[i] = bechValidator + } else { + validators = append(validators, bechValidator) + } + i++ + } + iterator.Close() + return validators[:i] // trim +} diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index b42418686465..841b73b4ceb6 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -63,40 +63,26 @@ func (k Keeper) validatorByPowerIndexExists(ctx sdk.Context, power []byte) bool return store.Get(power) != nil } -// Get the set of all validators with no limits, used during genesis dump -func (k Keeper) GetAllValidators(ctx sdk.Context) (validators []types.Validator) { - store := ctx.KVStore(k.storeKey) - iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey) - - i := 0 - for ; ; i++ { - if !iterator.Valid() { - break - } - addr := iterator.Key()[1:] - validator := types.MustUnmarshalValidator(k.cdc, addr, iterator.Value()) - validators = append(validators, validator) - iterator.Next() +// Get the set of all validators. If maxRetrieve is supplied, the respective amount will be returned. +func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve ...int16) (validators []types.Validator) { + retrieve := len(maxRetrieve) > 0 + if retrieve { + validators = make([]types.Validator, maxRetrieve[0]) } - iterator.Close() - return validators -} -// Get the set of all validators, retrieve a maxRetrieve number of records -func (k Keeper) GetValidators(ctx sdk.Context, maxRetrieve int16) (validators []types.Validator) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey) - validators = make([]types.Validator, maxRetrieve) i := 0 - for ; ; i++ { - if !iterator.Valid() || i > int(maxRetrieve-1) { - break - } + for ; iterator.Valid() && (!retrieve || (retrieve && i < int(maxRetrieve[0]))); iterator.Next() { addr := iterator.Key()[1:] validator := types.MustUnmarshalValidator(k.cdc, addr, iterator.Value()) - validators[i] = validator - iterator.Next() + if retrieve { + validators[i] = validator + } else { + validators = append(validators, validator) + } + i++ } iterator.Close() return validators[:i] // trim @@ -123,7 +109,7 @@ func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validat address := GetAddressFromValBondedIndexKey(iterator.Key()) validator, found := k.GetValidator(ctx, address) if !found { - panic(fmt.Sprintf("validator record not found for address: %v\n", address)) + panic(types.ErrNoValidatorFound(types.DefaultCodespace)) } validators[i] = validator @@ -142,20 +128,16 @@ func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator { validators := make([]types.Validator, maxValidators) iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) // largest to smallest i := 0 - for { - if !iterator.Valid() || i > int(maxValidators-1) { - break - } + for ; iterator.Valid() && i < int(maxValidators); iterator.Next() { address := iterator.Value() validator, found := k.GetValidator(ctx, address) if !found { - panic(fmt.Sprintf("validator record not found for address: %v\n", address)) + panic(types.ErrNoValidatorFound(types.DefaultCodespace)) } if validator.Status == sdk.Bonded { validators[i] = validator i++ } - iterator.Next() } iterator.Close() return validators[:i] // trim @@ -417,7 +399,7 @@ func (k Keeper) UpdateBondedValidators( var found bool validator, found = k.GetValidator(ctx, ownerAddr) if !found { - panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr)) + panic(types.ErrNoValidatorFound(types.DefaultCodespace)) } } diff --git a/x/stake/keeper/validator_test.go b/x/stake/keeper/validator_test.go index 2f471a2a5e70..1c89c6a67e43 100644 --- a/x/stake/keeper/validator_test.go +++ b/x/stake/keeper/validator_test.go @@ -43,7 +43,15 @@ func TestSetValidator(t *testing.T) { resVals = keeper.GetValidatorsByPower(ctx) require.Equal(t, 1, len(resVals)) - assert.True(ValEq(t, validator, resVals[0])) + require.True(ValEq(t, validator, resVals[0])) + + resVals = keeper.GetValidators(ctx) + require.Equal(t, 1, len(resVals)) + require.True(ValEq(t, validator, resVals[0])) + + resVals = keeper.GetValidators(ctx, 10) + require.Equal(t, 1, len(resVals)) + require.True(ValEq(t, validator, resVals[0])) updates := keeper.GetTendermintUpdates(ctx) require.Equal(t, 1, len(updates)) @@ -267,7 +275,10 @@ func TestValidatorBasics(t *testing.T) { _, found := keeper.GetValidator(ctx, addrVals[0]) require.False(t, found) resVals := keeper.GetValidatorsBonded(ctx) - assert.Zero(t, len(resVals)) + require.Zero(t, len(resVals)) + + resVals = keeper.GetValidators(ctx) + require.Zero(t, len(resVals)) pool = keeper.GetPool(ctx) assert.True(sdk.DecEq(t, sdk.ZeroDec(), pool.BondedTokens)) diff --git a/x/stake/queryable.go b/x/stake/queryable.go new file mode 100644 index 000000000000..a1fb339bffda --- /dev/null +++ b/x/stake/queryable.go @@ -0,0 +1,234 @@ +package stake + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" + keep "github.com/cosmos/cosmos-sdk/x/stake/keeper" + "github.com/cosmos/cosmos-sdk/x/stake/types" + abci "github.com/tendermint/tendermint/abci/types" +) + +// nolint +const ( + QueryValidators = "validators" + QueryValidator = "validator" + QueryDelegator = "delegator" + QueryDelegation = "delegation" + QueryUnbondingDelegation = "unbondingDelegation" + QueryDelegatorValidators = "delegatorValidators" + QueryDelegatorValidator = "delegatorValidator" + QueryPool = "pool" + QueryParameters = "parameters" +) + +// creates a querier for staking REST endpoints +func NewQuerier(k keep.Keeper, cdc *wire.Codec) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + switch path[0] { + case QueryValidators: + return queryValidators(ctx, cdc, path[1:], k) + case QueryValidator: + return queryValidator(ctx, cdc, path[1:], req, k) + case QueryDelegator: + return queryDelegator(ctx, cdc, path[1:], req, k) + case QueryDelegation: + return queryDelegation(ctx, cdc, path[1:], req, k) + case QueryUnbondingDelegation: + return queryUnbondingDelegation(ctx, cdc, path[1:], req, k) + case QueryDelegatorValidators: + return queryDelegatorValidators(ctx, cdc, path[1:], req, k) + case QueryDelegatorValidator: + return queryDelegatorValidator(ctx, cdc, path[1:], req, k) + case QueryPool: + return queryPool(ctx, cdc, k) + case QueryParameters: + return queryParameters(ctx, cdc, k) + default: + return nil, sdk.ErrUnknownRequest("unknown stake query endpoint") + } + } +} + +// defines the params for the following queries: +// - 'custom/stake/delegator' +// - 'custom/stake/delegatorValidators' +type QueryDelegatorParams struct { + DelegatorAddr sdk.AccAddress +} + +// defines the params for the following queries: +// - 'custom/stake/validator' +type QueryValidatorParams struct { + ValidatorAddr sdk.ValAddress +} + +// defines the params for the following queries: +// - 'custom/stake/delegation' +// - 'custom/stake/unbondingDelegation' +// - 'custom/stake/delegatorValidator' +type QueryBondsParams struct { + DelegatorAddr sdk.AccAddress + ValidatorAddr sdk.ValAddress +} + +func queryValidators(ctx sdk.Context, cdc *wire.Codec, path []string, k keep.Keeper) (res []byte, err sdk.Error) { + validators := k.GetBechValidators(ctx) + + res, errRes := wire.MarshalJSONIndent(cdc, validators) + if err != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) + } + return res, nil +} + +func queryValidator(ctx sdk.Context, cdc *wire.Codec, path []string, req abci.RequestQuery, k keep.Keeper) (res []byte, err sdk.Error) { + var params QueryValidatorParams + + errRes := cdc.UnmarshalJSON(req.Data, ¶ms) + if errRes != nil { + return []byte{}, sdk.ErrUnknownAddress(fmt.Sprintf("incorrectly formatted request address: %s", err.Error())) + } + + validator, found := k.GetValidator(ctx, params.ValidatorAddr) + if !found { + return []byte{}, ErrNoValidatorFound(DefaultCodespace) + } + + bechValidator, errRes := validator.Bech32Validator() + if err != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not bech32ify validator: %s", errRes.Error())) + } + + res, errRes = wire.MarshalJSONIndent(cdc, bechValidator) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) + } + return res, nil +} + +// TODO query with limit +func queryDelegator(ctx sdk.Context, cdc *wire.Codec, path []string, req abci.RequestQuery, k keep.Keeper) (res []byte, err sdk.Error) { + var params QueryDelegatorParams + errRes := cdc.UnmarshalJSON(req.Data, ¶ms) + if errRes != nil { + return []byte{}, sdk.ErrUnknownAddress(fmt.Sprintf("incorrectly formatted request address: %s", errRes.Error())) + } + delegations := k.GetDelegatorDelegationsREST(ctx, params.DelegatorAddr) + unbondingDelegations := k.GetUnbondingDelegations(ctx, params.DelegatorAddr) + redelegations := k.GetRedelegationsREST(ctx, params.DelegatorAddr) + + summary := types.DelegationSummary{ + Delegations: delegations, + UnbondingDelegations: unbondingDelegations, + Redelegations: redelegations, + } + + res, errRes = wire.MarshalJSONIndent(cdc, summary) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) + } + return res, nil +} + +// TODO query with limit +func queryDelegatorValidators(ctx sdk.Context, cdc *wire.Codec, path []string, req abci.RequestQuery, k keep.Keeper) (res []byte, err sdk.Error) { + var params QueryDelegatorParams + + errRes := cdc.UnmarshalJSON(req.Data, ¶ms) + if errRes != nil { + return []byte{}, sdk.ErrUnknownAddress(fmt.Sprintf("incorrectly formatted request address: %s", errRes.Error())) + } + + validators := k.GetDelegatorBechValidators(ctx, params.DelegatorAddr) + + res, errRes = wire.MarshalJSONIndent(cdc, validators) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) + } + return res, nil +} + +func queryDelegatorValidator(ctx sdk.Context, cdc *wire.Codec, path []string, req abci.RequestQuery, k keep.Keeper) (res []byte, err sdk.Error) { + var params QueryBondsParams + + errRes := cdc.UnmarshalJSON(req.Data, ¶ms) + if errRes != nil { + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request address: %s", errRes.Error())) + } + + validator := k.GetDelegatorValidator(ctx, params.DelegatorAddr, params.ValidatorAddr) + + bechValidator, errRes := validator.Bech32Validator() + if err != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not bech32ify validator: %s", errRes.Error())) + } + + res, errRes = wire.MarshalJSONIndent(cdc, bechValidator) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) + } + return res, nil +} + +func queryDelegation(ctx sdk.Context, cdc *wire.Codec, path []string, req abci.RequestQuery, k keep.Keeper) (res []byte, err sdk.Error) { + var params QueryBondsParams + + errRes := cdc.UnmarshalJSON(req.Data, ¶ms) + if errRes != nil { + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request address: %s", errRes.Error())) + } + + delegation, found := k.GetDelegation(ctx, params.DelegatorAddr, params.ValidatorAddr) + if !found { + return []byte{}, ErrNoDelegation(DefaultCodespace) + } + + delegationREST := delegation.ToRest() + res, errRes = wire.MarshalJSONIndent(cdc, delegationREST) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) + } + return res, nil +} + +func queryUnbondingDelegation(ctx sdk.Context, cdc *wire.Codec, path []string, req abci.RequestQuery, k keep.Keeper) (res []byte, err sdk.Error) { + var params QueryBondsParams + + errRes := cdc.UnmarshalJSON(req.Data, ¶ms) + if errRes != nil { + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request address: %s", errRes.Error())) + } + + unbond, found := k.GetUnbondingDelegation(ctx, params.DelegatorAddr, params.ValidatorAddr) + if !found { + return []byte{}, ErrNoUnbondingDelegation(DefaultCodespace) + } + + res, errRes = wire.MarshalJSONIndent(cdc, unbond) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) + } + return res, nil +} + +func queryPool(ctx sdk.Context, cdc *wire.Codec, k keep.Keeper) (res []byte, err sdk.Error) { + pool := k.GetPool(ctx) + + res, errRes := wire.MarshalJSONIndent(cdc, pool) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) + } + return res, nil +} + +func queryParameters(ctx sdk.Context, cdc *wire.Codec, k keep.Keeper) (res []byte, err sdk.Error) { + params := k.GetParams(ctx) + + res, errRes := wire.MarshalJSONIndent(cdc, params) + if errRes != nil { + return nil, sdk.ErrInternal(fmt.Sprintf("could not marshal result to JSON: %s", errRes.Error())) + } + return res, nil +} diff --git a/x/stake/queryable_test.go b/x/stake/queryable_test.go new file mode 100644 index 000000000000..c1d3ec1fa109 --- /dev/null +++ b/x/stake/queryable_test.go @@ -0,0 +1,209 @@ +package stake + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" + keep "github.com/cosmos/cosmos-sdk/x/stake/keeper" + "github.com/cosmos/cosmos-sdk/x/stake/types" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" +) + +var ( + addrAcc1, addrAcc2 = keep.Addrs[0], keep.Addrs[1] + addrVal1, addrVal2 = sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]) + pk1, pk2 = keep.PKs[0], keep.PKs[1] +) + +func newTestDelegatorQuery(delegatorAddr sdk.AccAddress) QueryDelegatorParams { + return QueryDelegatorParams{ + DelegatorAddr: delegatorAddr, + } +} + +func newTestValidatorQuery(validatorAddr sdk.ValAddress) QueryValidatorParams { + return QueryValidatorParams{ + ValidatorAddr: validatorAddr, + } +} + +func newTestBondQuery(delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) QueryBondsParams { + return QueryBondsParams{ + DelegatorAddr: delegatorAddr, + ValidatorAddr: validatorAddr, + } +} + +func TestQueryParametersPool(t *testing.T) { + cdc := wire.NewCodec() + ctx, _, keeper := keep.CreateTestInput(t, false, 1000) + + res, err := queryParameters(ctx, cdc, keeper) + require.Nil(t, err) + + var params types.Params + errRes := cdc.UnmarshalJSON(res, ¶ms) + require.Nil(t, errRes) + require.Equal(t, keeper.GetParams(ctx), params) + + res, err = queryPool(ctx, cdc, keeper) + require.Nil(t, err) + + var pool types.Pool + errRes = cdc.UnmarshalJSON(res, &pool) + require.Nil(t, errRes) + require.Equal(t, keeper.GetPool(ctx), pool) +} + +func TestQueryValidators(t *testing.T) { + cdc := wire.NewCodec() + ctx, _, keeper := keep.CreateTestInput(t, false, 10000) + + // Create Validators + msg1 := types.NewMsgCreateValidator(addrVal1, pk1, sdk.NewCoin("steak", sdk.NewInt(1000)), Description{}) + handleMsgCreateValidator(ctx, msg1, keeper) + msg2 := types.NewMsgCreateValidator(addrVal2, pk2, sdk.NewCoin("steak", sdk.NewInt(100)), Description{}) + handleMsgCreateValidator(ctx, msg2, keeper) + + // Query Validators + bechValidators := keeper.GetBechValidators(ctx) + res, err := queryValidators(ctx, cdc, []string{""}, keeper) + require.Nil(t, err) + + var validatorsResp []types.BechValidator + errRes := cdc.UnmarshalJSON(res, &validatorsResp) + require.Nil(t, errRes) + + require.Equal(t, len(bechValidators), len(validatorsResp)) + require.ElementsMatch(t, bechValidators, validatorsResp) + + // Query each validator + queryParams := newTestValidatorQuery(addrVal1) + bz, errRes := cdc.MarshalJSON(queryParams) + require.Nil(t, errRes) + + query := abci.RequestQuery{ + Path: "/custom/stake/validator", + Data: bz, + } + res, err = queryValidator(ctx, cdc, []string{query.Path}, query, keeper) + require.Nil(t, err) + + var bechValidator types.BechValidator + errRes = cdc.UnmarshalJSON(res, &bechValidator) + require.Nil(t, errRes) + + require.Equal(t, bechValidators[0], bechValidator) +} + +func TestQueryDelegation(t *testing.T) { + cdc := wire.NewCodec() + ctx, _, keeper := keep.CreateTestInput(t, false, 10000) + + // Create Validators and Delegation + msg1 := types.NewMsgCreateValidator(addrVal1, pk1, sdk.NewCoin("steak", sdk.NewInt(1000)), Description{}) + handleMsgCreateValidator(ctx, msg1, keeper) + msg2 := types.NewMsgDelegate(addrAcc2, addrVal1, sdk.NewCoin("steak", sdk.NewInt(20))) + handleMsgDelegate(ctx, msg2, keeper) + + // Query Delegator bonded validators + queryParams := newTestDelegatorQuery(addrAcc2) + bz, errRes := cdc.MarshalJSON(queryParams) + require.Nil(t, errRes) + + query := abci.RequestQuery{ + Path: "/custom/stake/delegatorValidators", + Data: bz, + } + + delValidators := keeper.GetDelegatorBechValidators(ctx, addrAcc2) + res, err := queryDelegatorValidators(ctx, cdc, []string{query.Path}, query, keeper) + require.Nil(t, err) + + var validatorsResp []types.BechValidator + errRes = cdc.UnmarshalJSON(res, &validatorsResp) + require.Nil(t, errRes) + + require.Equal(t, len(delValidators), len(validatorsResp)) + require.ElementsMatch(t, delValidators, validatorsResp) + + // Query bonded validator + queryBondParams := newTestBondQuery(addrAcc2, addrVal1) + bz, errRes = cdc.MarshalJSON(queryBondParams) + require.Nil(t, errRes) + + query = abci.RequestQuery{ + Path: "/custom/stake/delegatorValidator", + Data: bz, + } + + res, err = queryDelegatorValidator(ctx, cdc, []string{query.Path}, query, keeper) + require.Nil(t, err) + + var validator types.BechValidator + errRes = cdc.UnmarshalJSON(res, &validator) + require.Nil(t, errRes) + + require.Equal(t, delValidators[0], validator) + + // Query delegation + + query = abci.RequestQuery{ + Path: "/custom/stake/delegation", + Data: bz, + } + + delegation, found := keeper.GetDelegation(ctx, addrAcc2, addrVal1) + require.True(t, found) + + delegationREST := delegation.ToRest() + + res, err = queryDelegation(ctx, cdc, []string{query.Path}, query, keeper) + require.Nil(t, err) + + var delegationRestRes types.DelegationREST + errRes = cdc.UnmarshalJSON(res, &delegationRestRes) + require.Nil(t, errRes) + + require.Equal(t, delegationREST, delegationRestRes) + + // Query unbonging delegation + + msg3 := types.NewMsgBeginUnbonding(addrAcc2, addrVal1, sdk.NewDec(10)) + handleMsgBeginUnbonding(ctx, msg3, keeper) + + query = abci.RequestQuery{ + Path: "/custom/stake/unbondingDelegation", + Data: bz, + } + + unbond, found := keeper.GetUnbondingDelegation(ctx, addrAcc2, addrVal1) + require.True(t, found) + + res, err = queryUnbondingDelegation(ctx, cdc, []string{query.Path}, query, keeper) + require.Nil(t, err) + + var unbondRes types.UnbondingDelegation + errRes = cdc.UnmarshalJSON(res, &unbondRes) + require.Nil(t, errRes) + + require.Equal(t, unbond, unbondRes) + + // Query Delegator Summary + + query = abci.RequestQuery{ + Path: "/custom/stake/delegator", + Data: bz, + } + + res, err = queryDelegator(ctx, cdc, []string{query.Path}, query, keeper) + require.Nil(t, err) + + var summary types.DelegationSummary + errRes = cdc.UnmarshalJSON(res, &summary) + require.Nil(t, errRes) + + require.Equal(t, unbond, summary.UnbondingDelegations[0]) +} diff --git a/x/stake/stake.go b/x/stake/stake.go index 2782957ceb14..33f8c3864ca6 100644 --- a/x/stake/stake.go +++ b/x/stake/stake.go @@ -13,8 +13,11 @@ type ( BechValidator = types.BechValidator Description = types.Description Delegation = types.Delegation + DelegationREST = types.DelegationREST + DelegationSummary = types.DelegationSummary UnbondingDelegation = types.UnbondingDelegation Redelegation = types.Redelegation + RedelegationREST = types.RedelegationREST Params = types.Params Pool = types.Pool MsgCreateValidator = types.MsgCreateValidator diff --git a/x/stake/types/delegation.go b/x/stake/types/delegation.go index 5a2274c3aee9..e74344f291b0 100644 --- a/x/stake/types/delegation.go +++ b/x/stake/types/delegation.go @@ -24,6 +24,21 @@ type delegationValue struct { Height int64 } +// defines a delegation with string value for the shares +type DelegationREST struct { + DelegatorAddr string `json:"delegator_addr"` + ValidatorAddr string `json:"validator_addr"` + Shares string `json:"shares"` + Height int64 `json:"height"` +} + +// aggregates of all delegations, unbondings and redelegations +type DelegationSummary struct { + Delegations []DelegationREST `json:"delegations"` + UnbondingDelegations []UnbondingDelegation `json:"unbonding_delegations"` + Redelegations []RedelegationREST `json:"redelegations"` +} + // return the delegation without fields contained within the key for the store func MustMarshalDelegation(cdc *wire.Codec, delegation Delegation) []byte { val := delegationValue{ @@ -97,6 +112,16 @@ func (d Delegation) HumanReadableString() (string, error) { return resp, nil } +// changes delegation shares to string format +func (d Delegation) ToRest() DelegationREST { + return DelegationREST{ + DelegatorAddr: d.DelegatorAddr.String(), + ValidatorAddr: d.ValidatorAddr.String(), + Height: d.Height, + Shares: d.Shares.String(), + } +} + // UnbondingDelegation reflects a delegation's passive unbonding queue. type UnbondingDelegation struct { DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // delegator @@ -204,6 +229,19 @@ type redValue struct { SharesDst sdk.Dec } +// defines a redelegation with string value for the shares +type RedelegationREST struct { + DelegatorAddr string `json:"delegator_addr"` // delegator + ValidatorSrcAddr string `json:"validator_src_addr"` // validator redelegation source operator addr + ValidatorDstAddr string `json:"validator_dst_addr"` // validator redelegation destination operator addr + CreationHeight int64 `json:"creation_height"` // height which the redelegation took place + MinTime time.Time `json:"min_time"` // unix time for redelegation completion + InitialBalance sdk.Coin `json:"initial_balance"` // initial balance when redelegation started + Balance sdk.Coin `json:"balance"` // current balance + SharesSrc string `json:"shares_src"` // amount of source shares redelegating + SharesDst string `json:"shares_dst"` // amount of destination shares redelegating +} + // return the redelegation without fields contained within the key for the store func MustMarshalRED(cdc *wire.Codec, red Redelegation) []byte { val := redValue{ @@ -263,6 +301,20 @@ func (d Redelegation) Equal(d2 Redelegation) bool { return bytes.Equal(bz1, bz2) } +// changes redelegation shares to string format +func (d Redelegation) ToRest() RedelegationREST { + return RedelegationREST{ + DelegatorAddr: d.DelegatorAddr.String(), + ValidatorSrcAddr: d.ValidatorSrcAddr.String(), + ValidatorDstAddr: d.ValidatorDstAddr.String(), + CreationHeight: d.CreationHeight, + MinTime: d.MinTime, + InitialBalance: d.InitialBalance, + SharesSrc: d.SharesSrc.String(), + SharesDst: d.SharesDst.String(), + } +} + // HumanReadableString returns a human readable string representation of a // Redelegation. An error is returned if the UnbondingDelegation's delegator or // validator addresses cannot be Bech32 encoded. diff --git a/x/stake/types/delegation_test.go b/x/stake/types/delegation_test.go index 8e0dda7e247b..d1781548972c 100644 --- a/x/stake/types/delegation_test.go +++ b/x/stake/types/delegation_test.go @@ -30,6 +30,19 @@ func TestDelegationEqual(t *testing.T) { require.False(t, ok) } +func TestDelegationToRest(t *testing.T) { + d := Delegation{ + DelegatorAddr: sdk.AccAddress(addr1), + ValidatorAddr: addr2, + Shares: sdk.NewDec(100), + } + + rest := d.ToRest() + require.Equal(t, d.DelegatorAddr.String(), rest.DelegatorAddr) + require.Equal(t, d.ValidatorAddr.String(), rest.ValidatorAddr) + require.Equal(t, d.Shares.String(), rest.Shares) +} + func TestDelegationHumanReadableString(t *testing.T) { d := Delegation{ DelegatorAddr: sdk.AccAddress(addr1), @@ -115,3 +128,20 @@ func TestRedelegationHumanReadableString(t *testing.T) { require.Nil(t, err) require.NotEmpty(t, valStr) } + +func TestRedelegationToRest(t *testing.T) { + red := Redelegation{ + DelegatorAddr: sdk.AccAddress(addr1), + ValidatorSrcAddr: addr2, + ValidatorDstAddr: addr3, + SharesDst: sdk.NewDec(10), + SharesSrc: sdk.NewDec(20), + } + + rest := red.ToRest() + require.Equal(t, red.DelegatorAddr.String(), rest.DelegatorAddr) + require.Equal(t, red.ValidatorSrcAddr.String(), rest.ValidatorSrcAddr) + require.Equal(t, red.SharesSrc.String(), rest.SharesSrc) + require.Equal(t, red.ValidatorDstAddr.String(), rest.ValidatorDstAddr) + require.Equal(t, red.SharesDst.String(), rest.SharesDst) +} diff --git a/x/stake/types/validator.go b/x/stake/types/validator.go index 0d93093118e2..d7eef4f01c57 100644 --- a/x/stake/types/validator.go +++ b/x/stake/types/validator.go @@ -152,7 +152,7 @@ func (v Validator) HumanReadableString() (string, error) { } resp := "Validator \n" - resp += fmt.Sprintf("Operator: %s\n", v.Operator) + resp += fmt.Sprintf("Operator: %s\n", v.Operator.String()) resp += fmt.Sprintf("Validator: %s\n", bechConsPubKey) resp += fmt.Sprintf("Jailed: %v\n", v.Jailed) resp += fmt.Sprintf("Status: %s\n", sdk.BondStatusToString(v.Status)) @@ -174,9 +174,9 @@ func (v Validator) HumanReadableString() (string, error) { // validator struct for bech output type BechValidator struct { - Operator sdk.ValAddress `json:"operator"` // in bech32 - PubKey string `json:"pub_key"` // in bech32 - Jailed bool `json:"jailed"` // has the validator been jailed from bonded status? + Operator string `json:"operator"` // in bech32 + PubKey string `json:"pub_key"` // in bech32 + Jailed bool `json:"jailed"` // has the validator been jailed from bonded status? Status sdk.BondStatus `json:"status"` // validator status (bonded/unbonding/unbonded) Tokens sdk.Dec `json:"tokens"` // delegated tokens (incl. self-delegation) @@ -203,7 +203,7 @@ func (v Validator) Bech32Validator() (BechValidator, error) { } return BechValidator{ - Operator: v.Operator, + Operator: v.Operator.String(), PubKey: bechConsPubKey, Jailed: v.Jailed, diff --git a/x/stake/types/validator_test.go b/x/stake/types/validator_test.go index b81ae4458589..aebceff18262 100644 --- a/x/stake/types/validator_test.go +++ b/x/stake/types/validator_test.go @@ -24,6 +24,29 @@ func TestValidatorEqual(t *testing.T) { require.False(t, ok) } +func TestBech32Validator(t *testing.T) { + val1 := NewValidator(addr1, pk1, Description{}) + val2 := NewValidator(addr1, pk1, Description{}) + + val1Bech, err := val1.Bech32Validator() + require.Nil(t, err) + val2Bech, err := val2.Bech32Validator() + require.Nil(t, err) + + bechPubKey, err := sdk.Bech32ifyConsPub(val1.PubKey) // cosmosconspub... + require.Nil(t, err) + + require.Equal(t, val1Bech, val2Bech) + require.Equal(t, val1.Operator.String(), val1Bech.Operator) + require.Equal(t, bechPubKey, val2Bech.PubKey) + + val2 = NewValidator(addr2, pk2, Description{}) + val2Bech, err = val2.Bech32Validator() + require.Nil(t, err) + + require.NotEqual(t, val1Bech, val2Bech) +} + func TestUpdateDescription(t *testing.T) { d1 := Description{ Website: "https://validator.cosmos",