diff --git a/Gopkg.lock b/Gopkg.lock index 3aecfa090b..bcc306edb9 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -418,12 +418,12 @@ revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df" [[projects]] - digest = "1:10b3a599325740c84a7c81f3f3cb2e1fdb70b3ea01b7fa28495567a2519df431" + digest = "1:ad9c4c1a4e7875330b1f62906f2830f043a23edb5db997e3a5ac5d3e6eadf80a" name = "github.com/tendermint/go-amino" packages = ["."] pruneopts = "UT" - revision = "6dcc6ddc143e116455c94b25c1004c99e0d0ca12" - version = "v0.14.0" + revision = "dc14acf9ef15f85828bfbc561ed9dd9d2a284885" + version = "v0.14.1" [[projects]] digest = "1:9f8c4c93658315a795ffd3e0c943d39f78067dd8382b8d7bcfaf6686b92f3978" diff --git a/Gopkg.toml b/Gopkg.toml index 90f54c6570..f8ffcca054 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -28,7 +28,7 @@ [[override]] name = "github.com/tendermint/go-amino" - version = "v0.14.0" + version = "v0.14.1" [[override]] name = "github.com/tendermint/iavl" diff --git a/PENDING.md b/PENDING.md index 8c002af058..11a1e76135 100644 --- a/PENDING.md +++ b/PENDING.md @@ -5,6 +5,7 @@ BREAKING CHANGES * Gaia REST API (`gaiacli advanced rest-server`) * Gaia CLI (`gaiacli`) + * [cli] [\#2728](https://github.com/cosmos/cosmos-sdk/pull/2728) Seperate `tx` and `query` subcommands by module * [cli] [\#2727](https://github.com/cosmos/cosmos-sdk/pull/2727) Fix unbonding command flow * [cli] [\#2786](https://github.com/cosmos/cosmos-sdk/pull/2786) Fix redelegation command flow @@ -19,11 +20,18 @@ BREAKING CHANGES FEATURES * Gaia REST API (`gaiacli advanced rest-server`) + * [gov] [\#2479](https://github.com/cosmos/cosmos-sdk/issues/2479) Added governance parameter + query REST endpoints. * Gaia CLI (`gaiacli`) - + * [gov][cli] [\#2479](https://github.com/cosmos/cosmos-sdk/issues/2479) Added governance + parameter query commands. + * [stake][cli] [\#2027] Add CLI query command for getting all delegations to a specific validator. + * Gaia * [app] \#2791 Support export at a specific height, with `gaiad export --height=HEIGHT`. + * [x/gov] [#2479](https://github.com/cosmos/cosmos-sdk/issues/2479) Implemented querier + for getting governance parameters. * SDK * [simulator] \#2682 MsgEditValidator now looks at the validator's max rate, thus it now succeeds a significant portion of the time @@ -46,6 +54,7 @@ IMPROVEMENTS - [x/mock/simulation] [\#2720] major cleanup, introduction of helper objects, reorganization * Tendermint + - #2796 Update to go-amino 0.14.1 BUG FIXES diff --git a/client/config.go b/client/config.go index a1d38a016f..b766106387 100644 --- a/client/config.go +++ b/client/config.go @@ -3,20 +3,20 @@ package client import ( "bufio" "fmt" + "io/ioutil" + "os" + "path" + "github.com/cosmos/cosmos-sdk/types" "github.com/mitchellh/go-homedir" "github.com/pelletier/go-toml" "github.com/spf13/cobra" - "io/ioutil" - "os" - "path" ) type cliConfig struct { Home string `toml:"home"` ChainID string `toml:"chain_id"` TrustNode bool `toml:"trust_node"` - Encoding string `toml:"encoding"` Output string `toml:"output"` Node string `toml:"node"` Trace bool `toml:"trace"` @@ -41,23 +41,24 @@ func runConfigCmd(cmd *cobra.Command, args []string) error { } stdin := BufferStdin() + gaiaCLIHome, err := handleGaiaCLIHome(home, stdin) if err != nil { return err } + node, err := handleNode(stdin) if err != nil { return err } + trustNode, err := handleTrustNode(stdin) if err != nil { return err } - encoding := "btc" - output := "text" - var chainID string - chainID, err = types.DefaultChainID() + chainID, err := types.DefaultChainID() + if err != nil { fmt.Println("Couldn't populate ChainID, so using an empty one.") } @@ -66,8 +67,7 @@ func runConfigCmd(cmd *cobra.Command, args []string) error { Home: gaiaCLIHome, ChainID: chainID, TrustNode: trustNode, - Encoding: encoding, - Output: output, + Output: "text", Node: node, Trace: false, } diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 9b042832b9..1be908e98b 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -539,7 +539,7 @@ func TestBonding(t *testing.T) { require.Equal(t, int64(40), coins.AmountOf(denom).Int64()) - // query validator + // query delegation bond := getDelegation(t, port, addr, operAddrs[0]) require.Equal(t, amt, bond.Shares) @@ -547,6 +547,10 @@ func TestBonding(t *testing.T) { require.Len(t, delegatorDels, 1) require.Equal(t, amt, delegatorDels[0].Shares) + // query all delegations to validator + bonds := getValidatorDelegations(t, port, operAddrs[0]) + require.Len(t, bonds, 2) + bondedValidators := getDelegatorValidators(t, port, addr) require.Len(t, bondedValidators, 1) require.Equal(t, operAddrs[0], bondedValidators[0].OperatorAddr) @@ -731,26 +735,33 @@ func TestProposalsQuery(t *testing.T) { cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addrs[0], addrs[1]}) defer cleanup() + depositParam := getDepositParam(t, port) + halfMinDeposit := depositParam.MinDeposit.AmountOf(stakeTypes.DefaultBondDenom).Int64() / 2 + getVotingParam(t, port) + getTallyingParam(t, port) + // Addr1 proposes (and deposits) proposals #1 and #2 - resultTx := doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], 5) + resultTx := doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], halfMinDeposit) var proposalID1 uint64 cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID1) tests.WaitForHeight(resultTx.Height+1, port) - resultTx = doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], 5) + + resultTx = doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], halfMinDeposit) var proposalID2 uint64 cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID2) tests.WaitForHeight(resultTx.Height+1, port) // Addr2 proposes (and deposits) proposals #3 - resultTx = doSubmitProposal(t, port, seeds[1], names[1], passwords[1], addrs[1], 5) + resultTx = doSubmitProposal(t, port, seeds[1], names[1], passwords[1], addrs[1], halfMinDeposit) var proposalID3 uint64 cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID3) tests.WaitForHeight(resultTx.Height+1, port) // Addr2 deposits on proposals #2 & #3 - resultTx = doDeposit(t, port, seeds[1], names[1], passwords[1], addrs[1], proposalID2, 5) + resultTx = doDeposit(t, port, seeds[1], names[1], passwords[1], addrs[1], proposalID2, halfMinDeposit) tests.WaitForHeight(resultTx.Height+1, port) - resultTx = doDeposit(t, port, seeds[1], names[1], passwords[1], addrs[1], proposalID3, 5) + + resultTx = doDeposit(t, port, seeds[1], names[1], passwords[1], addrs[1], proposalID3, halfMinDeposit) tests.WaitForHeight(resultTx.Height+1, port) // check deposits match proposal and individual deposits @@ -1207,6 +1218,17 @@ func getValidator(t *testing.T, port string, validatorAddr sdk.ValAddress) stake return validator } +func getValidatorDelegations(t *testing.T, port string, validatorAddr sdk.ValAddress) []stake.Delegation { + res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s/delegations", validatorAddr.String()), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var delegations []stake.Delegation + err := cdc.UnmarshalJSON([]byte(body), &delegations) + require.Nil(t, err) + + return delegations +} + func getValidatorUnbondingDelegations(t *testing.T, port string, validatorAddr sdk.ValAddress) []stake.UnbondingDelegation { res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s/unbonding_delegations", validatorAddr.String()), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) @@ -1231,6 +1253,36 @@ func getValidatorRedelegations(t *testing.T, port string, validatorAddr sdk.ValA // ============= Governance Module ================ +func getDepositParam(t *testing.T, port string) gov.DepositParams { + res, body := Request(t, port, "GET", "/gov/parameters/deposit", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var depositParams gov.DepositParams + err := cdc.UnmarshalJSON([]byte(body), &depositParams) + require.Nil(t, err) + return depositParams +} + +func getVotingParam(t *testing.T, port string) gov.VotingParams { + res, body := Request(t, port, "GET", "/gov/parameters/voting", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var votingParams gov.VotingParams + err := cdc.UnmarshalJSON([]byte(body), &votingParams) + require.Nil(t, err) + return votingParams +} + +func getTallyingParam(t *testing.T, port string) gov.TallyParams { + res, body := Request(t, port, "GET", "/gov/parameters/tallying", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var tallyParams gov.TallyParams + err := cdc.UnmarshalJSON([]byte(body), &tallyParams) + require.Nil(t, err) + return tallyParams +} + func getProposal(t *testing.T, port string, proposalID uint64) gov.Proposal { res, body := Request(t, port, "GET", fmt.Sprintf("/gov/proposals/%d", proposalID), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) diff --git a/client/lcd/swagger-ui/swagger.yaml b/client/lcd/swagger-ui/swagger.yaml index 86b6ec6813..920194ffa4 100644 --- a/client/lcd/swagger-ui/swagger.yaml +++ b/client/lcd/swagger-ui/swagger.yaml @@ -950,6 +950,30 @@ paths: description: Invalid validator address 500: description: Internal Server Error + /stake/validators/{validatorAddr}/delegations: + parameters: + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string + get: + summary: Get all delegations from a validator + tags: + - ICS21 + produces: + - application/json + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/Delegation" + 400: + description: Invalid validator address + 500: + description: Internal Server Error /stake/validators/{validatorAddr}/unbonding_delegations: parameters: - in: path @@ -1427,7 +1451,7 @@ paths: /gov/proposals/{proposalId}/votes/{voter}: get: summary: Query vote - description: Query vote information by proposalId and voter address + description: Query vote information by proposal Id and voter address produces: - application/json tags: @@ -1454,6 +1478,83 @@ paths: description: Found no vote 500: description: Internal Server Error + /gov/parameters/deposit: + get: + summary: Query governance deposit parameters + description: Query governance deposit parameters. The max_deposit_period units are in nanoseconds. + produces: + - application/json + tags: + - ICS22 + responses: + 200: + description: OK + schema: + type: object + properties: + min_deposit: + type: array + items: + $ref: "#/definitions/Coin" + max_deposit_period: + type: string + example: "86400000000000" + 400: + description: is not a valid query request path + 404: + description: Found no deposit parameters + 500: + description: Internal Server Error + /gov/parameters/tallying: + get: + summary: Query governance tally parameters + description: Query governance tally parameters + produces: + - application/json + tags: + - ICS22 + responses: + 200: + description: OK + schema: + properties: + threshold: + type: string + example: "0.5000000000" + veto: + type: string + example: "0.3340000000" + governance_penalty: + type: string + example: "0.0100000000" + 400: + description: is not a valid query request path + 404: + description: Found no tally parameters + 500: + description: Internal Server Error + /gov/parameters/voting: + get: + summary: Query governance voting parameters + description: Query governance voting parameters. The voting_period units are in nanoseconds. + produces: + - application/json + tags: + - ICS22 + responses: + 200: + description: OK + schema: + properties: + voting_period: + type: string + example: "86400000000000" + 400: + description: is not a valid query request path + 404: + description: Found no voting parameters + 500: + description: Internal Server Error definitions: CheckTxResult: diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 283550a16a..774fd4d6ad 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -4,9 +4,7 @@ import ( "bytes" "encoding/json" "fmt" - "github.com/cosmos/cosmos-sdk/x/stake" - stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" - "github.com/tendermint/tendermint/crypto/secp256k1" + "io/ioutil" "net" "net/http" @@ -16,6 +14,9 @@ import ( "strings" "testing" + stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types" + "github.com/tendermint/tendermint/crypto/secp256k1" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/keys" gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app" @@ -25,6 +26,7 @@ import ( "github.com/cosmos/cosmos-sdk/tests" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/stake" "github.com/spf13/viper" "github.com/stretchr/testify/require" diff --git a/client/rpc/root.go b/client/rpc/root.go index 74c5a69a2e..7bdcd8cd84 100644 --- a/client/rpc/root.go +++ b/client/rpc/root.go @@ -22,15 +22,8 @@ func todoNotImplemented(_ *cobra.Command, _ []string) error { return errors.New("todo: Command not yet implemented") } -// AddCommands adds a number of rpc-related subcommands -func AddCommands(cmd *cobra.Command) { - cmd.AddCommand( - initClientCommand(), - statusCommand(), - ) -} - -func initClientCommand() *cobra.Command { +// InitClientCommand initializes client commands +func InitClientCommand() *cobra.Command { cmd := &cobra.Command{ Use: "init", Short: "Initialize light client", diff --git a/client/rpc/status.go b/client/rpc/status.go index 68716f3a0b..0f81fc9c6f 100644 --- a/client/rpc/status.go +++ b/client/rpc/status.go @@ -14,7 +14,8 @@ import ( ctypes "github.com/tendermint/tendermint/rpc/core/types" ) -func statusCommand() *cobra.Command { +// StatusCommand returns the status of the network +func StatusCommand() *cobra.Command { cmd := &cobra.Command{ Use: "status", Short: "Query remote node for status", diff --git a/client/tx/root.go b/client/tx/root.go index 0f237b5967..5cd7eca0b8 100644 --- a/client/tx/root.go +++ b/client/tx/root.go @@ -2,20 +2,11 @@ package tx import ( "github.com/gorilla/mux" - "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" ) -// AddCommands adds a number of tx-query related subcommands -func AddCommands(cmd *cobra.Command, cdc *codec.Codec) { - cmd.AddCommand( - SearchTxCmd(cdc), - QueryTxCmd(cdc), - ) -} - // register REST routes func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { r.HandleFunc("/txs/{hash}", QueryTxRequestHandlerFn(cdc, cliCtx)).Methods("GET") diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 07cbbab786..874c2a29a4 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -237,7 +237,7 @@ func TestGaiaCLICreateValidator(t *testing.T) { initialPool.BondedTokens = initialPool.BondedTokens.Add(sdk.NewDec(100)) // Delegate tx on GaiaAppGenState // create validator - cvStr := fmt.Sprintf("gaiacli tx create-validator %v", flags) + cvStr := fmt.Sprintf("gaiacli tx stake create-validator %v", flags) cvStr += fmt.Sprintf(" --from=%s", "bar") cvStr += fmt.Sprintf(" --pubkey=%s", barCeshPubKey) cvStr += fmt.Sprintf(" --amount=%v", fmt.Sprintf("2%s", stakeTypes.DefaultBondDenom)) @@ -268,12 +268,16 @@ func TestGaiaCLICreateValidator(t *testing.T) { barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", barAddr, flags)) require.Equal(t, int64(8), barAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64(), "%v", barAcc) - validator := executeGetValidator(t, fmt.Sprintf("gaiacli query validator %s --output=json %v", sdk.ValAddress(barAddr), flags)) + validator := executeGetValidator(t, fmt.Sprintf("gaiacli query stake validator %s --output=json %v", sdk.ValAddress(barAddr), flags)) require.Equal(t, validator.OperatorAddr, sdk.ValAddress(barAddr)) require.True(sdk.DecEq(t, sdk.NewDec(2), validator.Tokens)) + validatorDelegations := executeGetValidatorDelegations(t, fmt.Sprintf("gaiacli query stake delegations-to %s --output=json %v", sdk.ValAddress(barAddr), flags)) + require.Len(t, validatorDelegations, 1) + require.NotZero(t, validatorDelegations[0].Shares) + // unbond a single share - unbondStr := fmt.Sprintf("gaiacli tx unbond begin %v", flags) + unbondStr := fmt.Sprintf("gaiacli tx stake unbond begin %v", flags) unbondStr += fmt.Sprintf(" --from=%s", "bar") unbondStr += fmt.Sprintf(" --validator=%s", sdk.ValAddress(barAddr)) unbondStr += fmt.Sprintf(" --shares-amount=%v", "1") @@ -286,19 +290,19 @@ func TestGaiaCLICreateValidator(t *testing.T) { barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %v %v", barCech, flags)) require.Equal(t, int64(9), barAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64(), "%v", barAcc) */ - validator = executeGetValidator(t, fmt.Sprintf("gaiacli query validator %s --output=json %v", sdk.ValAddress(barAddr), flags)) + validator = executeGetValidator(t, fmt.Sprintf("gaiacli query stake validator %s --output=json %v", sdk.ValAddress(barAddr), flags)) require.Equal(t, "1.0000000000", validator.Tokens.String()) validatorUbds := executeGetValidatorUnbondingDelegations(t, - fmt.Sprintf("gaiacli query unbonding-delegations-from %s --output=json %v", + fmt.Sprintf("gaiacli query stake unbonding-delegations-from %s --output=json %v", sdk.ValAddress(barAddr), flags)) require.Len(t, validatorUbds, 1) require.Equal(t, "1", validatorUbds[0].Balance.Amount.String()) - params := executeGetParams(t, fmt.Sprintf("gaiacli query parameters --output=json %v", flags)) + params := executeGetParams(t, fmt.Sprintf("gaiacli query stake parameters --output=json %v", flags)) require.True(t, defaultParams.Equal(params)) - pool := executeGetPool(t, fmt.Sprintf("gaiacli query pool --output=json %v", flags)) + pool := executeGetPool(t, fmt.Sprintf("gaiacli query stake pool --output=json %v", flags)) require.Equal(t, initialPool.BondedTokens, pool.BondedTokens) } @@ -313,16 +317,20 @@ func TestGaiaCLISubmitProposal(t *testing.T) { tests.WaitForTMStart(port) tests.WaitForNextNBlocksTM(2, port) + executeGetDepositParam(t, fmt.Sprintf("gaiacli query gov param deposit %v", flags)) + executeGetVotingParam(t, fmt.Sprintf("gaiacli query gov param voting %v", flags)) + executeGetTallyingParam(t, fmt.Sprintf("gaiacli query gov param tallying %v", flags)) + fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show foo --output=json --home=%s", gaiacliHome)) fooAcc := executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) - proposalsQuery, _ := tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals %v", flags), "") + proposalsQuery, _ := tests.ExecuteT(t, fmt.Sprintf("gaiacli query gov proposals %v", flags), "") require.Equal(t, "No matching proposals found", proposalsQuery) // submit a test proposal - spStr := fmt.Sprintf("gaiacli tx submit-proposal %v", flags) + spStr := fmt.Sprintf("gaiacli tx gov submit-proposal %v", flags) spStr += fmt.Sprintf(" --from=%s", "foo") spStr += fmt.Sprintf(" --deposit=%s", fmt.Sprintf("5%s", stakeTypes.DefaultBondDenom)) spStr += fmt.Sprintf(" --type=%s", "Text") @@ -349,19 +357,19 @@ func TestGaiaCLISubmitProposal(t *testing.T) { fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) require.Equal(t, int64(45), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) - proposal1 := executeGetProposal(t, fmt.Sprintf("gaiacli query proposal --proposal-id=1 --output=json %v", flags)) + proposal1 := executeGetProposal(t, fmt.Sprintf("gaiacli query gov proposal --proposal-id=1 --output=json %v", flags)) require.Equal(t, uint64(1), proposal1.GetProposalID()) require.Equal(t, gov.StatusDepositPeriod, proposal1.GetStatus()) - proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals %v", flags), "") + proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query gov proposals %v", flags), "") require.Equal(t, " 1 - Test", proposalsQuery) deposit := executeGetDeposit(t, - fmt.Sprintf("gaiacli query deposit --proposal-id=1 --depositer=%s --output=json %v", + fmt.Sprintf("gaiacli query gov deposit --proposal-id=1 --depositer=%s --output=json %v", fooAddr, flags)) require.Equal(t, int64(5), deposit.Amount.AmountOf(stakeTypes.DefaultBondDenom).Int64()) - depositStr := fmt.Sprintf("gaiacli tx deposit %v", flags) + depositStr := fmt.Sprintf("gaiacli tx gov deposit %v", flags) depositStr += fmt.Sprintf(" --from=%s", "foo") depositStr += fmt.Sprintf(" --deposit=%s", fmt.Sprintf("10%s", stakeTypes.DefaultBondDenom)) depositStr += fmt.Sprintf(" --proposal-id=%s", "1") @@ -381,22 +389,23 @@ func TestGaiaCLISubmitProposal(t *testing.T) { // test query deposit deposits := executeGetDeposits(t, - fmt.Sprintf("gaiacli query deposits --proposal-id=1 --output=json %v", flags)) + fmt.Sprintf("gaiacli query gov deposits --proposal-id=1 --output=json %v", flags)) require.Len(t, deposits, 1) require.Equal(t, int64(15), deposits[0].Amount.AmountOf(stakeTypes.DefaultBondDenom).Int64()) deposit = executeGetDeposit(t, - fmt.Sprintf("gaiacli query deposit --proposal-id=1 --depositer=%s --output=json %v", + fmt.Sprintf("gaiacli query gov deposit --proposal-id=1 --depositer=%s --output=json %v", fooAddr, flags)) require.Equal(t, int64(15), deposit.Amount.AmountOf(stakeTypes.DefaultBondDenom).Int64()) fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli query account %s %v", fooAddr, flags)) + require.Equal(t, int64(35), fooAcc.GetCoins().AmountOf(stakeTypes.DefaultBondDenom).Int64()) - proposal1 = executeGetProposal(t, fmt.Sprintf("gaiacli query proposal --proposal-id=1 --output=json %v", flags)) + proposal1 = executeGetProposal(t, fmt.Sprintf("gaiacli query gov proposal --proposal-id=1 --output=json %v", flags)) require.Equal(t, uint64(1), proposal1.GetProposalID()) require.Equal(t, gov.StatusVotingPeriod, proposal1.GetStatus()) - voteStr := fmt.Sprintf("gaiacli tx vote %v", flags) + voteStr := fmt.Sprintf("gaiacli tx gov vote %v", flags) voteStr += fmt.Sprintf(" --from=%s", "foo") voteStr += fmt.Sprintf(" --proposal-id=%s", "1") voteStr += fmt.Sprintf(" --option=%s", "Yes") @@ -414,23 +423,23 @@ func TestGaiaCLISubmitProposal(t *testing.T) { executeWrite(t, voteStr, app.DefaultKeyPass) tests.WaitForNextNBlocksTM(2, port) - vote := executeGetVote(t, fmt.Sprintf("gaiacli query vote --proposal-id=1 --voter=%s --output=json %v", fooAddr, flags)) + vote := executeGetVote(t, fmt.Sprintf("gaiacli query gov vote --proposal-id=1 --voter=%s --output=json %v", fooAddr, flags)) require.Equal(t, uint64(1), vote.ProposalID) require.Equal(t, gov.OptionYes, vote.Option) - votes := executeGetVotes(t, fmt.Sprintf("gaiacli query votes --proposal-id=1 --output=json %v", flags)) + votes := executeGetVotes(t, fmt.Sprintf("gaiacli query gov votes --proposal-id=1 --output=json %v", flags)) require.Len(t, votes, 1) require.Equal(t, uint64(1), votes[0].ProposalID) require.Equal(t, gov.OptionYes, votes[0].Option) - proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --status=DepositPeriod %v", flags), "") + proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query gov proposals --status=DepositPeriod %v", flags), "") require.Equal(t, "No matching proposals found", proposalsQuery) - proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --status=VotingPeriod %v", flags), "") + proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query gov proposals --status=VotingPeriod %v", flags), "") require.Equal(t, " 1 - Test", proposalsQuery) // submit a second test proposal - spStr = fmt.Sprintf("gaiacli tx submit-proposal %v", flags) + spStr = fmt.Sprintf("gaiacli tx gov submit-proposal %v", flags) spStr += fmt.Sprintf(" --from=%s", "foo") spStr += fmt.Sprintf(" --deposit=%s", fmt.Sprintf("5%s", stakeTypes.DefaultBondDenom)) spStr += fmt.Sprintf(" --type=%s", "Text") @@ -440,7 +449,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { executeWrite(t, spStr, app.DefaultKeyPass) tests.WaitForNextNBlocksTM(2, port) - proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query proposals --limit=1 %v", flags), "") + proposalsQuery, _ = tests.ExecuteT(t, fmt.Sprintf("gaiacli query gov proposals --limit=1 %v", flags), "") require.Equal(t, " 2 - Apples", proposalsQuery) } @@ -553,7 +562,6 @@ func TestGaiaCLIConfig(t *testing.T) { config, err := ioutil.ReadFile(path.Join(gaiacliHome, "config", "config.toml")) require.NoError(t, err) expectedConfig := fmt.Sprintf(`chain_id = "%s" -encoding = "btc" home = "%s" node = "%s" output = "text" @@ -572,7 +580,6 @@ trust_node = true // ensure it works without an initialized gaiad state expectedConfig = fmt.Sprintf(`chain_id = "" -encoding = "btc" home = "%s" node = "%s" output = "text" @@ -751,6 +758,15 @@ func executeGetValidatorRedelegations(t *testing.T, cmdStr string) []stake.Redel return reds } +func executeGetValidatorDelegations(t *testing.T, cmdStr string) []stake.Delegation { + out, _ := tests.ExecuteT(t, cmdStr, "") + var delegations []stake.Delegation + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &delegations) + require.NoError(t, err, "out %v\n, err %v", out, err) + return delegations +} + func executeGetPool(t *testing.T, cmdStr string) stake.Pool { out, _ := tests.ExecuteT(t, cmdStr, "") var pool stake.Pool @@ -772,6 +788,33 @@ func executeGetParams(t *testing.T, cmdStr string) stake.Params { //___________________________________________________________________________________ // gov +func executeGetDepositParam(t *testing.T, cmdStr string) gov.DepositParams { + out, _ := tests.ExecuteT(t, cmdStr, "") + var depositParam gov.DepositParams + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &depositParam) + require.NoError(t, err, "out %v\n, err %v", out, err) + return depositParam +} + +func executeGetVotingParam(t *testing.T, cmdStr string) gov.VotingParams { + out, _ := tests.ExecuteT(t, cmdStr, "") + var votingParam gov.VotingParams + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &votingParam) + require.NoError(t, err, "out %v\n, err %v", out, err) + return votingParam +} + +func executeGetTallyingParam(t *testing.T, cmdStr string) gov.TallyParams { + out, _ := tests.ExecuteT(t, cmdStr, "") + var tallyingParam gov.TallyParams + cdc := app.MakeCodec() + err := cdc.UnmarshalJSON([]byte(out), &tallyingParam) + require.NoError(t, err, "out %v\n, err %v", out, err) + return tallyingParam +} + func executeGetProposal(t *testing.T, cmdStr string) gov.Proposal { out, _ := tests.ExecuteT(t, cmdStr, "") var proposal gov.Proposal diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index 212a670214..de99b0fc8f 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -13,18 +13,10 @@ import ( "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/lcd" "github.com/cosmos/cosmos-sdk/client/rpc" - "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" - authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" - bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" - distrcmd "github.com/cosmos/cosmos-sdk/x/distribution/client/cli" - govcmd "github.com/cosmos/cosmos-sdk/x/gov/client/cli" - slashingcmd "github.com/cosmos/cosmos-sdk/x/slashing/client/cli" - stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli" - _ "github.com/cosmos/cosmos-sdk/client/lcd/statik" ) @@ -40,7 +32,7 @@ const ( var ( rootCmd = &cobra.Command{ Use: "gaiacli", - Short: "Gaia light-client", + Short: "Command line interface for interacting with gaiad", } ) @@ -57,90 +49,23 @@ func main() { // TODO: setup keybase, viper object, etc. to be passed into // the below functions and eliminate global vars, like we do // with the cdc - rootCmd.AddCommand(client.ConfigCmd()) - // add standard rpc commands - rpc.AddCommands(rootCmd) - - //Add query commands - queryCmd := &cobra.Command{ - Use: "query", - Aliases: []string{"q"}, - Short: "Querying subcommands", - } - queryCmd.AddCommand( - rpc.BlockCommand(), - rpc.ValidatorCommand(), - ) - tx.AddCommands(queryCmd, cdc) - queryCmd.AddCommand(client.LineBreak) - queryCmd.AddCommand(client.GetCommands( - authcmd.GetAccountCmd(storeAcc, cdc, authcmd.GetAccountDecoder(cdc)), - stakecmd.GetCmdQueryDelegation(storeStake, cdc), - stakecmd.GetCmdQueryDelegations(storeStake, cdc), - stakecmd.GetCmdQueryUnbondingDelegation(storeStake, cdc), - stakecmd.GetCmdQueryUnbondingDelegations(storeStake, cdc), - stakecmd.GetCmdQueryRedelegation(storeStake, cdc), - stakecmd.GetCmdQueryRedelegations(storeStake, cdc), - stakecmd.GetCmdQueryValidator(storeStake, cdc), - stakecmd.GetCmdQueryValidators(storeStake, cdc), - stakecmd.GetCmdQueryValidatorUnbondingDelegations(queryRouteStake, cdc), - stakecmd.GetCmdQueryValidatorRedelegations(queryRouteStake, cdc), - stakecmd.GetCmdQueryParams(storeStake, cdc), - stakecmd.GetCmdQueryPool(storeStake, cdc), - govcmd.GetCmdQueryProposal(storeGov, cdc), - govcmd.GetCmdQueryProposals(storeGov, cdc), - govcmd.GetCmdQueryVote(storeGov, cdc), - govcmd.GetCmdQueryVotes(storeGov, cdc), - govcmd.GetCmdQueryDeposit(storeGov, cdc), - govcmd.GetCmdQueryDeposits(storeGov, cdc), - slashingcmd.GetCmdQuerySigningInfo(storeSlashing, cdc), - )...) - - //Add query commands - txCmd := &cobra.Command{ - Use: "tx", - Short: "Transactions subcommands", - } - - //Add auth and bank commands - txCmd.AddCommand( - client.PostCommands( - bankcmd.GetBroadcastCommand(cdc), - authcmd.GetSignCommand(cdc, authcmd.GetAccountDecoder(cdc)), - )...) - txCmd.AddCommand(client.LineBreak) - - txCmd.AddCommand( - client.PostCommands( - stakecmd.GetCmdCreateValidator(cdc), - stakecmd.GetCmdEditValidator(cdc), - stakecmd.GetCmdDelegate(cdc), - stakecmd.GetCmdRedelegate(storeStake, cdc), - stakecmd.GetCmdUnbond(storeStake, cdc), - distrcmd.GetCmdWithdrawRewards(cdc), - distrcmd.GetCmdSetWithdrawAddr(cdc), - govcmd.GetCmdDeposit(cdc), - bankcmd.SendTxCmd(cdc), - govcmd.GetCmdSubmitProposal(cdc), - slashingcmd.GetCmdUnjail(cdc), - govcmd.GetCmdVote(cdc), - )...) + // Construct Root Command rootCmd.AddCommand( - queryCmd, - txCmd, + rpc.InitClientCommand(), + rpc.StatusCommand(), + client.ConfigCmd(), + queryCmd(cdc), + txCmd(cdc), + client.LineBreak, lcd.ServeCommand(cdc), client.LineBreak, - ) - - // add proxy, version and key info - rootCmd.AddCommand( keys.Commands(), client.LineBreak, version.VersionCmd, ) - // prepare and add flags + // Add flags and prefix all env exposed with GA executor := cli.PrepareMainCmd(rootCmd, "GA", app.DefaultCLIHome) err := initConfig(rootCmd) if err != nil { diff --git a/cmd/gaia/cmd/gaiacli/query.go b/cmd/gaia/cmd/gaiacli/query.go new file mode 100644 index 0000000000..3a806d36c2 --- /dev/null +++ b/cmd/gaia/cmd/gaiacli/query.go @@ -0,0 +1,83 @@ +package main + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/rpc" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/spf13/cobra" + + authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + govcmd "github.com/cosmos/cosmos-sdk/x/gov/client/cli" + slashingcmd "github.com/cosmos/cosmos-sdk/x/slashing/client/cli" + stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli" + amino "github.com/tendermint/go-amino" +) + +func queryCmd(cdc *amino.Codec) *cobra.Command { + //Add query commands + queryCmd := &cobra.Command{ + Use: "query", + Aliases: []string{"q"}, + Short: "Querying subcommands", + } + + // Group staking queries under a subcommand + stakeQueryCmd := &cobra.Command{ + Use: "stake", + Short: "Querying commands for the staking module", + } + + stakeQueryCmd.AddCommand(client.GetCommands( + stakecmd.GetCmdQueryDelegation(storeStake, cdc), + stakecmd.GetCmdQueryDelegations(storeStake, cdc), + stakecmd.GetCmdQueryUnbondingDelegation(storeStake, cdc), + stakecmd.GetCmdQueryUnbondingDelegations(storeStake, cdc), + stakecmd.GetCmdQueryRedelegation(storeStake, cdc), + stakecmd.GetCmdQueryRedelegations(storeStake, cdc), + stakecmd.GetCmdQueryValidator(storeStake, cdc), + stakecmd.GetCmdQueryValidators(storeStake, cdc), + stakecmd.GetCmdQueryValidatorDelegations(storeStake, cdc), + stakecmd.GetCmdQueryValidatorUnbondingDelegations(queryRouteStake, cdc), + stakecmd.GetCmdQueryValidatorRedelegations(queryRouteStake, cdc), + stakecmd.GetCmdQueryParams(storeStake, cdc), + stakecmd.GetCmdQueryPool(storeStake, cdc))...) + + // Group gov queries under a subcommand + govQueryCmd := &cobra.Command{ + Use: "gov", + Short: "Querying commands for the governance module", + } + + govQueryCmd.AddCommand(client.GetCommands( + govcmd.GetCmdQueryProposal(storeGov, cdc), + govcmd.GetCmdQueryProposals(storeGov, cdc), + govcmd.GetCmdQueryVote(storeGov, cdc), + govcmd.GetCmdQueryVotes(storeGov, cdc), + govcmd.GetCmdQueryParams(storeGov, cdc), + govcmd.GetCmdQueryDeposit(storeGov, cdc), + govcmd.GetCmdQueryDeposits(storeGov, cdc))...) + + // Group slashing queries under a subcommand + slashingQueryCmd := &cobra.Command{ + Use: "slashing", + Short: "Querying commands for the slashing module", + } + + slashingQueryCmd.AddCommand(client.GetCommands( + slashingcmd.GetCmdQuerySigningInfo(storeSlashing, cdc))...) + + // Query commcmmand structure + queryCmd.AddCommand( + rpc.BlockCommand(), + rpc.ValidatorCommand(), + tx.SearchTxCmd(cdc), + tx.QueryTxCmd(cdc), + client.LineBreak, + client.GetCommands(authcmd.GetAccountCmd(storeAcc, cdc, authcmd.GetAccountDecoder(cdc)))[0], + stakeQueryCmd, + govQueryCmd, + slashingQueryCmd, + ) + + return queryCmd +} diff --git a/cmd/gaia/cmd/gaiacli/tx.go b/cmd/gaia/cmd/gaiacli/tx.go new file mode 100644 index 0000000000..fa0abc4ad8 --- /dev/null +++ b/cmd/gaia/cmd/gaiacli/tx.go @@ -0,0 +1,83 @@ +package main + +import ( + "github.com/cosmos/cosmos-sdk/client" + "github.com/spf13/cobra" + + authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" + distrcmd "github.com/cosmos/cosmos-sdk/x/distribution/client/cli" + govcmd "github.com/cosmos/cosmos-sdk/x/gov/client/cli" + slashingcmd "github.com/cosmos/cosmos-sdk/x/slashing/client/cli" + stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli" + amino "github.com/tendermint/go-amino" +) + +func txCmd(cdc *amino.Codec) *cobra.Command { + //Add transaction generation commands + txCmd := &cobra.Command{ + Use: "tx", + Short: "Transactions subcommands", + } + + stakeTxCmd := &cobra.Command{ + Use: "stake", + Short: "Staking transaction subcommands", + } + + stakeTxCmd.AddCommand(client.PostCommands( + stakecmd.GetCmdCreateValidator(cdc), + stakecmd.GetCmdEditValidator(cdc), + stakecmd.GetCmdDelegate(cdc), + stakecmd.GetCmdRedelegate(storeStake, cdc), + stakecmd.GetCmdUnbond(storeStake, cdc), + )...) + + distTxCmd := &cobra.Command{ + Use: "dist", + Short: "Distribution transactions subcommands", + } + + distTxCmd.AddCommand(client.PostCommands( + distrcmd.GetCmdWithdrawRewards(cdc), + distrcmd.GetCmdSetWithdrawAddr(cdc), + )...) + + govTxCmd := &cobra.Command{ + Use: "gov", + Short: "Governance transactions subcommands", + } + + govTxCmd.AddCommand(client.PostCommands( + govcmd.GetCmdDeposit(cdc), + govcmd.GetCmdVote(cdc), + govcmd.GetCmdSubmitProposal(cdc), + )...) + + slashingTxCmd := &cobra.Command{ + Use: "slashing", + Short: "Slashing transactions subcommands", + } + + slashingTxCmd.AddCommand(client.PostCommands( + slashingcmd.GetCmdUnjail(cdc), + )...) + + txCmd.AddCommand( + //Add auth and bank commands + client.PostCommands( + bankcmd.SendTxCmd(cdc), + bankcmd.GetBroadcastCommand(cdc), + authcmd.GetSignCommand(cdc, authcmd.GetAccountDecoder(cdc)), + )...) + + txCmd.AddCommand( + client.LineBreak, + stakeTxCmd, + distTxCmd, + govTxCmd, + slashingTxCmd, + ) + + return txCmd +} diff --git a/docs/sdk/clients.md b/docs/sdk/clients.md index 4f0669183f..3c0ca5c501 100644 --- a/docs/sdk/clients.md +++ b/docs/sdk/clients.md @@ -341,6 +341,13 @@ Additionally, as you can get all the outgoing redelegations from a particular va To get previous redelegation(s) status on past blocks, try adding the `--height` flag. +##### Query Delegations To Validator + +You can also query all of the delegations to a particular validator: +```bash + gaiacli query delegations-to +``` + ### Governance Governance is the process from which users in the Cosmos Hub can come to consensus on software upgrades, parameters of the mainnet or on custom text proposals. This is done through voting on proposals, which will be submitted by `Atom` holders on the mainnet. diff --git a/docs/spec/governance/README.md b/docs/spec/governance/README.md index 0ddbc7cfdc..6cc1edb508 100644 --- a/docs/spec/governance/README.md +++ b/docs/spec/governance/README.md @@ -2,7 +2,7 @@ ## Abstract -This paper specifies the Governance module of the Cosmos-SDK, which was first described in the [Cosmos Whitepaper](https://cosmos.network/about/whitepaper) in June 2016. +This paper specifies the Governance module of the Cosmos-SDK, which was first described in the [Cosmos Whitepaper](https://cosmos.network/about/whitepaper) in June 2016. The module enables Cosmos-SDK based blockchain to support an on-chain governance system. In this system, holders of the native staking token of the chain can vote on proposals on a 1 token 1 vote basis. Next is a list of features the module currently supports: @@ -24,7 +24,7 @@ The following specification uses *Atom* as the native staking token. The module 1. **[Design overview](overview.md)** 2. **Implementation** 1. **[State](state.md)** - 1. Procedures + 1. Parameters 2. Proposals 3. Proposal Processing Queue 2. **[Transactions](transactions.md)** diff --git a/docs/spec/governance/state.md b/docs/spec/governance/state.md index ca130bd4c3..3858ba06b6 100644 --- a/docs/spec/governance/state.md +++ b/docs/spec/governance/state.md @@ -2,36 +2,35 @@ ## State -### Procedures and base types - -`Procedures` define the rule according to which votes are run. There can only -be one active procedure at any given time. If governance wants to change a -procedure, either to modify a value or add/remove a parameter, a new procedure -has to be created and the previous one rendered inactive. +### Parameters and base types +`Parameters` define the rules according to which votes are run. There can only +be one active parameter set at any given time. If governance wants to change a +parameter set, either to modify a value or add/remove a parameter field, a new +parameter set has to be created and the previous one rendered inactive. ```go -type DepositProcedure struct { - MinDeposit sdk.Coins // Minimum deposit for a proposal to enter voting period. - MaxDepositPeriod time.Time // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months +type DepositParams struct { + MinDeposit sdk.Coins // Minimum deposit for a proposal to enter voting period. + MaxDepositPeriod time.Time // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months } ``` ```go -type VotingProcedure struct { - VotingPeriod time.Time // Length of the voting period. Initial value: 2 weeks +type VotingParams struct { + VotingPeriod time.Time // Length of the voting period. Initial value: 2 weeks } ``` ```go -type TallyingProcedure struct { - Threshold sdk.Dec // Minimum propotion of Yes votes for proposal to pass. Initial value: 0.5 - Veto sdk.Dec // Minimum proportion of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3 - GovernancePenalty sdk.Dec // Penalty if validator does not vote +type TallyParams struct { + Threshold sdk.Dec // Minimum proportion of Yes votes for proposal to pass. Initial value: 0.5 + Veto sdk.Dec // Minimum proportion of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3 + GovernancePenalty sdk.Dec // Penalty if validator does not vote } ``` -Procedures are stored in a global `GlobalParams` KVStore. +Parameters are stored in a global `GlobalParams` KVStore. Additionally, we introduce some basic types: @@ -61,7 +60,7 @@ const ( ProposalStatusActive = 0x2 // MinDeposit is reachhed, participants can vote ProposalStatusAccepted = 0x3 // Proposal has been accepted ProposalStatusRejected = 0x4 // Proposal has been rejected - ProposalStatusClosed. = 0x5 // Proposal never reached MinDeposit + ProposalStatusClosed = 0x5 // Proposal never reached MinDeposit ) ``` @@ -76,7 +75,7 @@ const ( ### ValidatorGovInfo -This type is used in a temp map when tallying +This type is used in a temp map when tallying ```go type ValidatorGovInfo struct { @@ -87,7 +86,7 @@ This type is used in a temp map when tallying ### Proposals -`Proposals` are an item to be voted on. +`Proposals` are an item to be voted on. ```go type Proposal struct { @@ -99,7 +98,7 @@ type Proposal struct { SubmitTime time.Time // Time of the block where TxGovSubmitProposal was included DepositEndTime time.Time // Time that the DepositPeriod of a proposal would expire Submitter sdk.AccAddress // Address of the submitter - + VotingStartTime time.Time // Time of the block where MinDeposit was reached. time.Time{} if MinDeposit is not reached VotingEndTime time.Time // Time of the block that the VotingPeriod for a proposal will end. CurrentStatus ProposalStatus // Current status of the proposal @@ -135,7 +134,7 @@ For pseudocode purposes, here are the two function we will use to read or write ### Proposal Processing Queue **Store:** -* `ProposalProcessingQueue`: A queue `queue[proposalID]` containing all the +* `ProposalProcessingQueue`: A queue `queue[proposalID]` containing all the `ProposalIDs` of proposals that reached `MinDeposit`. Each `EndBlock`, all the proposals that have reached the end of their voting period are processed. To process a finished proposal, the application tallies the votes, compute the votes of @@ -145,8 +144,8 @@ For pseudocode purposes, here are the two function we will use to read or write And the pseudocode for the `ProposalProcessingQueue`: ```go - in EndBlock do - + in EndBlock do + for finishedProposalID in GetAllFinishedProposalIDs(block.Time) proposal = load(Governance, ) // proposal is a const key @@ -171,7 +170,7 @@ And the pseudocode for the `ProposalProcessingQueue`: if (isVal) tmpValMap(voterAddress).Vote = vote - tallyingProcedure = load(GlobalParams, 'TallyingProcedure') + tallyingParam = load(GlobalParams, 'TallyingParam') // Update tally if validator voted they voted for each validator in validators @@ -182,14 +181,14 @@ And the pseudocode for the `ProposalProcessingQueue`: // Check if proposal is accepted or rejected totalNonAbstain := proposal.YesVotes + proposal.NoVotes + proposal.NoWithVetoVotes - if (proposal.Votes.YesVotes/totalNonAbstain > tallyingProcedure.Threshold AND proposal.Votes.NoWithVetoVotes/totalNonAbstain < tallyingProcedure.Veto) + if (proposal.Votes.YesVotes/totalNonAbstain > tallyingParam.Threshold AND proposal.Votes.NoWithVetoVotes/totalNonAbstain < tallyingParam.Veto) // proposal was accepted at the end of the voting period // refund deposits (non-voters already punished) proposal.CurrentStatus = ProposalStatusAccepted for each (amount, depositer) in proposal.Deposits depositer.AtomBalance += amount - else + else // proposal was rejected proposal.CurrentStatus = ProposalStatusRejected diff --git a/docs/spec/governance/transactions.md b/docs/spec/governance/transactions.md index f3636facda..68966bcfc4 100644 --- a/docs/spec/governance/transactions.md +++ b/docs/spec/governance/transactions.md @@ -4,7 +4,7 @@ ### Proposal Submission -Proposals can be submitted by any Atom holder via a `TxGovSubmitProposal` +Proposals can be submitted by any Atom holder via a `TxGovSubmitProposal` transaction. ```go @@ -24,7 +24,7 @@ type TxGovSubmitProposal struct { * If `MinDeposit` is reached: * Push `proposalID` in `ProposalProcessingQueue` -A `TxGovSubmitProposal` transaction can be handled according to the following +A `TxGovSubmitProposal` transaction can be handled according to the following pseudocode. ```go @@ -32,31 +32,31 @@ pseudocode. // Check if TxGovSubmitProposal is valid. If it is, create proposal // upon receiving txGovSubmitProposal from sender do - - if !correctlyFormatted(txGovSubmitProposal) + + if !correctlyFormatted(txGovSubmitProposal) // check if proposal is correctly formatted. Includes fee payment. throw - - initialDeposit = txGovSubmitProposal.InitialDeposit - if (initialDeposit.Atoms <= 0) OR (sender.AtomBalance < initialDeposit.Atoms) + + initialDeposit = txGovSubmitProposal.InitialDeposit + if (initialDeposit.Atoms <= 0) OR (sender.AtomBalance < initialDeposit.Atoms) // InitialDeposit is negative or null OR sender has insufficient funds throw if (txGovSubmitProposal.Type != ProposalTypePlainText) OR (txGovSubmitProposal.Type != ProposalTypeSoftwareUpgrade) - + sender.AtomBalance -= initialDeposit.Atoms - depositProcedure = load(GlobalParams, 'DepositProcedure') - + depositParam = load(GlobalParams, 'DepositParam') + proposalID = generate new proposalID proposal = NewProposal() - + proposal.Title = txGovSubmitProposal.Title proposal.Description = txGovSubmitProposal.Description proposal.Type = txGovSubmitProposal.Type proposal.TotalDeposit = initialDeposit proposal.SubmitTime = - proposal.DepositEndTime = .Add(depositProcedure.MaxDepositPeriod) + proposal.DepositEndTime = .Add(depositParam.MaxDepositPeriod) proposal.Deposits.append({initialDeposit, sender}) proposal.Submitter = sender proposal.YesVotes = 0 @@ -64,15 +64,15 @@ upon receiving txGovSubmitProposal from sender do proposal.NoWithVetoVotes = 0 proposal.AbstainVotes = 0 proposal.CurrentStatus = ProposalStatusOpen - + store(Proposals, , proposal) // Store proposal in Proposals mapping return proposalID ``` ### Deposit -Once a proposal is submitted, if -`Proposal.TotalDeposit < ActiveProcedure.MinDeposit`, Atom holders can send +Once a proposal is submitted, if +`Proposal.TotalDeposit < ActiveParam.MinDeposit`, Atom holders can send `TxGovDeposit` transactions to increase the proposal's deposit. ```go @@ -89,7 +89,7 @@ type TxGovDeposit struct { * If `MinDeposit` is reached: * Push `proposalID` in `ProposalProcessingQueueEnd` -A `TxGovDeposit` transaction has to go through a number of checks to be valid. +A `TxGovDeposit` transaction has to go through a number of checks to be valid. These checks are outlined in the following pseudocode. ```go @@ -98,27 +98,27 @@ These checks are outlined in the following pseudocode. upon receiving txGovDeposit from sender do // check if proposal is correctly formatted. Includes fee payment. - - if !correctlyFormatted(txGovDeposit) + + if !correctlyFormatted(txGovDeposit) throw - + proposal = load(Proposals, ) // proposal is a const key, proposalID is variable - if (proposal == nil) + if (proposal == nil) // There is no proposal for this proposalID throw - + if (txGovDeposit.Deposit.Atoms <= 0) ORĀ (sender.AtomBalance < txGovDeposit.Deposit.Atoms) OR (proposal.CurrentStatus != ProposalStatusOpen) - // deposit is negative or null + // deposit is negative or null // OR sender has insufficient funds // OR proposal is not open for deposit anymore throw - depositProcedure = load(GlobalParams, 'DepositProcedure') + depositParam = load(GlobalParams, 'DepositParam') - if (CurrentBlock >= proposal.SubmitBlock + depositProcedure.MaxDepositPeriod) + if (CurrentBlock >= proposal.SubmitBlock + depositParam.MaxDepositPeriod) proposal.CurrentStatus = ProposalStatusClosed else @@ -127,21 +127,21 @@ upon receiving txGovDeposit from sender do proposal.Deposits.append({txGovVote.Deposit, sender}) proposal.TotalDeposit.Plus(txGovDeposit.Deposit) - - if (proposal.TotalDeposit >= depositProcedure.MinDeposit) + + if (proposal.TotalDeposit >= depositParam.MinDeposit) // MinDeposit is reached, vote opens - + proposal.VotingStartBlock = CurrentBlock proposal.CurrentStatus = ProposalStatusActive - ProposalProcessingQueue.push(txGovDeposit.ProposalID) + ProposalProcessingQueue.push(txGovDeposit.ProposalID) store(Proposals, , proposal) ``` ### Vote -Once `ActiveProcedure.MinDeposit` is reached, voting period starts. From there, -bonded Atom holders are able to send `TxGovVote` transactions to cast their +Once `ActiveParam.MinDeposit` is reached, voting period starts. From there, +bonded Atom holders are able to send `TxGovVote` transactions to cast their vote on the proposal. ```go @@ -157,25 +157,25 @@ vote on the proposal. *Note: Gas cost for this message has to take into account the future tallying of the vote in EndBlocker* -Next is a pseudocode proposal of the way `TxGovVote` transactions are +Next is a pseudocode proposal of the way `TxGovVote` transactions are handled: ```go // PSEUDOCODE // // Check if TxGovVote is valid. If it is, count vote// - + upon receiving txGovVote from sender do - // check if proposal is correctly formatted. Includes fee payment. - - if !correctlyFormatted(txGovDeposit) + // check if proposal is correctly formatted. Includes fee payment. + + if !correctlyFormatted(txGovDeposit) throw - + proposal = load(Proposals, ) - if (proposal == nil) + if (proposal == nil) // There is no proposal for this proposalID throw - + if (proposal.CurrentStatus == ProposalStatusActive) diff --git a/examples/basecoin/cmd/basecli/main.go b/examples/basecoin/cmd/basecli/main.go index 1732e82eb9..4ebd1bef84 100644 --- a/examples/basecoin/cmd/basecli/main.go +++ b/examples/basecoin/cmd/basecli/main.go @@ -39,10 +39,14 @@ func main() { // with the cdc. // add standard rpc, and tx commands - rpc.AddCommands(rootCmd) - rootCmd.AddCommand(client.LineBreak) - tx.AddCommands(rootCmd, cdc) - rootCmd.AddCommand(client.LineBreak) + rootCmd.AddCommand( + rpc.InitClientCommand(), + rpc.StatusCommand(), + client.LineBreak, + tx.SearchTxCmd(cdc), + tx.QueryTxCmd(cdc), + client.LineBreak, + ) // add query/post commands (custom to binary) rootCmd.AddCommand( diff --git a/examples/democoin/cli_test/cli_test.go b/examples/democoin/cli_test/cli_test.go index 8df97c987e..9a3cba0df7 100644 --- a/examples/democoin/cli_test/cli_test.go +++ b/examples/democoin/cli_test/cli_test.go @@ -3,10 +3,11 @@ package clitest import ( "encoding/json" "fmt" - "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "os" "testing" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/tests" "github.com/stretchr/testify/require" diff --git a/examples/democoin/cmd/democli/main.go b/examples/democoin/cmd/democli/main.go index 6bb2da8680..4121fa30b5 100644 --- a/examples/democoin/cmd/democli/main.go +++ b/examples/democoin/cmd/democli/main.go @@ -52,10 +52,15 @@ func main() { // with the cdc // add standard rpc, and tx commands - rpc.AddCommands(rootCmd) - rootCmd.AddCommand(client.LineBreak) - tx.AddCommands(rootCmd, cdc) - rootCmd.AddCommand(client.LineBreak) + + rootCmd.AddCommand( + rpc.InitClientCommand(), + rpc.StatusCommand(), + client.LineBreak, + tx.SearchTxCmd(cdc), + tx.QueryTxCmd(cdc), + client.LineBreak, + ) // add query/post commands (custom to binary) // start with commands common to basecoin diff --git a/x/gov/client/cli/tx.go b/x/gov/client/cli/tx.go index 455bd58db5..fc25f5ec4f 100644 --- a/x/gov/client/cli/tx.go +++ b/x/gov/client/cli/tx.go @@ -249,6 +249,30 @@ func GetCmdVote(cdc *codec.Codec) *cobra.Command { return cmd } +// GetCmdQueryProposal implements the query proposal command. +func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "param [param-type]", + Short: "Query the parameters (voting|tallying|deposit) of the governance process", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + paramType := args[0] + + cliCtx := context.NewCLIContext().WithCodec(cdc) + + res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/params/%s", queryRoute, paramType), nil) + if err != nil { + return err + } + + fmt.Println(string(res)) + return nil + }, + } + + return cmd +} + // GetCmdQueryProposal implements the query proposal command. func GetCmdQueryProposal(queryRoute string, cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index f477f2ff9a..930a39b025 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -18,7 +18,8 @@ import ( // REST Variable names // nolint const ( - RestProposalID = "proposalId" + RestParamsType = "type" + RestProposalID = "proposal-id" RestDepositer = "depositer" RestVoter = "voter" RestProposalStatus = "status" @@ -32,6 +33,11 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits", RestProposalID), depositHandlerFn(cdc, cliCtx)).Methods("POST") r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/votes", RestProposalID), voteHandlerFn(cdc, cliCtx)).Methods("POST") + r.HandleFunc( + fmt.Sprintf("/gov/parameters/{%s}", RestParamsType), + queryParamsHandlerFn(cdc, cliCtx), + ).Methods("GET") + r.HandleFunc("/gov/proposals", queryProposalsWithParameterFn(cdc, cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}", RestProposalID), queryProposalHandlerFn(cdc, cliCtx)).Methods("GET") r.HandleFunc(fmt.Sprintf("/gov/proposals/{%s}/deposits", RestProposalID), queryDepositsHandlerFn(cdc, cliCtx)).Methods("GET") @@ -177,6 +183,21 @@ func voteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc } } +func queryParamsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + paramType := vars[RestParamsType] + + res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/gov/%s/%s", gov.QueryParams, paramType), nil) + if err != nil { + utils.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + return + } + + utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + } +} + func queryProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) diff --git a/x/gov/keeper.go b/x/gov/keeper.go index 862db82744..4f370ef531 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -250,7 +250,7 @@ func (keeper Keeper) GetDepositParams(ctx sdk.Context) DepositParams { return depositParams } -// Returns the current Voting Procedure from the global param store +// Returns the current VotingParams from the global param store // nolint: errcheck func (keeper Keeper) GetVotingParams(ctx sdk.Context) VotingParams { var votingParams VotingParams @@ -258,7 +258,7 @@ func (keeper Keeper) GetVotingParams(ctx sdk.Context) VotingParams { return votingParams } -// Returns the current Tallying Procedure from the global param store +// Returns the current TallyParam from the global param store // nolint: errcheck func (keeper Keeper) GetTallyParams(ctx sdk.Context) TallyParams { var tallyParams TallyParams diff --git a/x/gov/params.go b/x/gov/params.go index 4d8126d141..b9e1efa878 100644 --- a/x/gov/params.go +++ b/x/gov/params.go @@ -6,20 +6,20 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// Procedure around Deposits for governance +// Param around Deposits for governance type DepositParams struct { MinDeposit sdk.Coins `json:"min_deposit"` // Minimum deposit for a proposal to enter voting period. MaxDepositPeriod time.Duration `json:"max_deposit_period"` // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months } -// Procedure around Tallying votes in governance +// Param around Tallying votes in governance type TallyParams struct { Threshold sdk.Dec `json:"threshold"` // Minimum propotion of Yes votes for proposal to pass. Initial value: 0.5 Veto sdk.Dec `json:"veto"` // Minimum value of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3 GovernancePenalty sdk.Dec `json:"governance_penalty"` // Penalty if validator does not vote } -// Procedure around Voting in governance +// Param around Voting in governance type VotingParams struct { VotingPeriod time.Duration `json:"voting_period"` // Length of the voting period. } diff --git a/x/gov/queryable.go b/x/gov/querier.go similarity index 86% rename from x/gov/queryable.go rename to x/gov/querier.go index 3e70d43aa0..171ab469d5 100644 --- a/x/gov/queryable.go +++ b/x/gov/querier.go @@ -1,6 +1,8 @@ package gov import ( + "fmt" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" abci "github.com/tendermint/tendermint/abci/types" @@ -8,6 +10,7 @@ import ( // query endpoints supported by the governance Querier const ( + QueryParams = "params" QueryProposals = "proposals" QueryProposal = "proposal" QueryDeposits = "deposits" @@ -15,11 +18,17 @@ const ( QueryVotes = "votes" QueryVote = "vote" QueryTally = "tally" + + ParamDeposit = "deposit" + ParamVoting = "voting" + ParamTallying = "tallying" ) 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 QueryParams: + return queryParams(ctx, path[1:], req, keeper) case QueryProposals: return queryProposals(ctx, path[1:], req, keeper) case QueryProposal: @@ -40,6 +49,31 @@ func NewQuerier(keeper Keeper) sdk.Querier { } } +func queryParams(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + switch path[0] { + case ParamDeposit: + bz, err2 := codec.MarshalJSONIndent(keeper.cdc, keeper.GetDepositParams(ctx)) + if err2 != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err2.Error())) + } + return bz, nil + case ParamVoting: + bz, err2 := codec.MarshalJSONIndent(keeper.cdc, keeper.GetVotingParams(ctx)) + if err2 != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err2.Error())) + } + return bz, nil + case ParamTallying: + bz, err2 := codec.MarshalJSONIndent(keeper.cdc, keeper.GetTallyParams(ctx)) + if err2 != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err2.Error())) + } + return bz, nil + default: + return res, sdk.ErrUnknownRequest(fmt.Sprintf("%s is not a valid query request path", req.Path)) + } +} + // Params for query 'custom/gov/proposal' type QueryProposalParams struct { ProposalID uint64 diff --git a/x/gov/querier_test.go b/x/gov/querier_test.go new file mode 100644 index 0000000000..eee58d2ac1 --- /dev/null +++ b/x/gov/querier_test.go @@ -0,0 +1,306 @@ +package gov + +import ( + "strings" + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" +) + +func getQueriedParams(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier) (DepositParams, VotingParams, TallyParams) { + query := abci.RequestQuery{ + Path: strings.Join([]string{"custom", "gov", QueryParams, ParamDeposit}, "/"), + Data: []byte{}, + } + + bz, err := querier(ctx, []string{QueryParams, ParamDeposit}, query) + require.Nil(t, err) + require.NotNil(t, bz) + + var depositParams DepositParams + err2 := cdc.UnmarshalJSON(bz, &depositParams) + require.Nil(t, err2) + + query = abci.RequestQuery{ + Path: strings.Join([]string{"custom", "gov", QueryParams, ParamVoting}, "/"), + Data: []byte{}, + } + + bz, err = querier(ctx, []string{QueryParams, ParamVoting}, query) + require.Nil(t, err) + require.NotNil(t, bz) + + var votingParams VotingParams + err2 = cdc.UnmarshalJSON(bz, &votingParams) + require.Nil(t, err2) + + query = abci.RequestQuery{ + Path: strings.Join([]string{"custom", "gov", QueryParams, ParamTallying}, "/"), + Data: []byte{}, + } + + bz, err = querier(ctx, []string{QueryParams, ParamTallying}, query) + require.Nil(t, err) + require.NotNil(t, bz) + + var tallyParams TallyParams + err2 = cdc.UnmarshalJSON(bz, &tallyParams) + require.Nil(t, err2) + + return depositParams, votingParams, tallyParams +} + +func getQueriedProposal(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) Proposal { + query := abci.RequestQuery{ + Path: strings.Join([]string{"custom", "gov", QueryProposal}, "/"), + Data: cdc.MustMarshalJSON(QueryProposalParams{ + ProposalID: proposalID, + }), + } + + bz, err := querier(ctx, []string{QueryProposal}, query) + require.Nil(t, err) + require.NotNil(t, bz) + + var proposal Proposal + err2 := cdc.UnmarshalJSON(bz, proposal) + require.Nil(t, err2) + return proposal +} + +func getQueriedProposals(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, depositer, voter sdk.AccAddress, status ProposalStatus, limit uint64) []Proposal { + query := abci.RequestQuery{ + Path: strings.Join([]string{"custom", "gov", QueryProposals}, "/"), + Data: cdc.MustMarshalJSON(QueryProposalsParams{ + Voter: voter, + Depositer: depositer, + ProposalStatus: status, + Limit: limit, + }), + } + + bz, err := querier(ctx, []string{QueryProposal}, query) + require.Nil(t, err) + require.NotNil(t, bz) + + var proposals []Proposal + err2 := cdc.UnmarshalJSON(bz, proposals) + require.Nil(t, err2) + return proposals +} + +func getQueriedDeposit(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64, depositer sdk.AccAddress) Deposit { + query := abci.RequestQuery{ + Path: strings.Join([]string{"custom", "gov", QueryDeposit}, "/"), + Data: cdc.MustMarshalJSON(QueryDepositParams{ + ProposalID: proposalID, + Depositer: depositer, + }), + } + + bz, err := querier(ctx, []string{QueryDeposits}, query) + require.Nil(t, err) + require.NotNil(t, bz) + + var deposit Deposit + err2 := cdc.UnmarshalJSON(bz, deposit) + require.Nil(t, err2) + return deposit +} + +func getQueriedDeposits(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) []Deposit { + query := abci.RequestQuery{ + Path: strings.Join([]string{"custom", "gov", QueryDeposits}, "/"), + Data: cdc.MustMarshalJSON(QueryDepositsParams{ + ProposalID: proposalID, + }), + } + + bz, err := querier(ctx, []string{QueryDeposits}, query) + require.Nil(t, err) + require.NotNil(t, bz) + + var deposits []Deposit + err2 := cdc.UnmarshalJSON(bz, &deposits) + require.Nil(t, err2) + return deposits +} + +func getQueriedVote(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64, voter sdk.AccAddress) Vote { + query := abci.RequestQuery{ + Path: strings.Join([]string{"custom", "gov", QueryVote}, "/"), + Data: cdc.MustMarshalJSON(QueryVoteParams{ + ProposalID: proposalID, + Voter: voter, + }), + } + + bz, err := querier(ctx, []string{QueryVote}, query) + require.Nil(t, err) + require.NotNil(t, bz) + + var vote Vote + err2 := cdc.UnmarshalJSON(bz, &vote) + require.Nil(t, err2) + return vote +} + +func getQueriedVotes(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) []Vote { + query := abci.RequestQuery{ + Path: strings.Join([]string{"custom", "gov", QueryVote}, "/"), + Data: cdc.MustMarshalJSON(QueryVotesParams{ + ProposalID: proposalID, + }), + } + + bz, err := querier(ctx, []string{QueryVotes}, query) + require.Nil(t, err) + require.NotNil(t, bz) + + var votes []Vote + err2 := cdc.UnmarshalJSON(bz, &votes) + require.Nil(t, err2) + return votes +} + +func getQueriedTally(t *testing.T, ctx sdk.Context, cdc *codec.Codec, querier sdk.Querier, proposalID uint64) TallyResult { + query := abci.RequestQuery{ + Path: strings.Join([]string{"custom", "gov", QueryTally}, "/"), + Data: cdc.MustMarshalJSON(QueryTallyParams{ + ProposalID: proposalID, + }), + } + + bz, err := querier(ctx, []string{QueryTally}, query) + require.Nil(t, err) + require.NotNil(t, bz) + + var tally TallyResult + err2 := cdc.UnmarshalJSON(bz, &tally) + require.Nil(t, err2) + return tally +} + +func testQueryParams(t *testing.T) { + cdc := codec.New() + mapp, keeper, _, _, _, _ := getMockApp(t, 1000) + querier := NewQuerier(keeper) + ctx := mapp.NewContext(false, abci.Header{}) + + getQueriedParams(t, ctx, cdc, querier) +} + +func testQueries(t *testing.T) { + cdc := codec.New() + mapp, keeper, _, addrs, _, _ := getMockApp(t, 1000) + querier := NewQuerier(keeper) + handler := NewHandler(keeper) + ctx := mapp.NewContext(false, abci.Header{}) + + depositParams, _, _ := getQueriedParams(t, ctx, cdc, querier) + + // addrs[0] proposes (and deposits) proposals #1 and #2 + res := handler(ctx, NewMsgSubmitProposal("title", "description", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin("dummycoin", 1)})) + var proposalID1 uint64 + cdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &proposalID1) + + res = handler(ctx, NewMsgSubmitProposal("title", "description", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin("dummycoin", 1)})) + var proposalID2 uint64 + cdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &proposalID2) + + // addrs[1] proposes (and deposits) proposals #3 + res = handler(ctx, NewMsgSubmitProposal("title", "description", ProposalTypeText, addrs[1], sdk.Coins{sdk.NewInt64Coin("dummycoin", 1)})) + var proposalID3 uint64 + cdc.MustUnmarshalBinaryLengthPrefixed(res.Data, &proposalID3) + + // addrs[1] deposits on proposals #2 & #3 + res = handler(ctx, NewMsgDeposit(addrs[1], proposalID2, depositParams.MinDeposit)) + res = handler(ctx, NewMsgDeposit(addrs[1], proposalID3, depositParams.MinDeposit)) + + // check deposits on proposal1 match individual deposits + deposits := getQueriedDeposits(t, ctx, cdc, querier, proposalID1) + require.Len(t, deposits, 1) + deposit := getQueriedDeposit(t, ctx, cdc, querier, proposalID1, addrs[0]) + require.Equal(t, deposit, deposits[0]) + + // check deposits on proposal2 match individual deposits + deposits = getQueriedDeposits(t, ctx, cdc, querier, proposalID2) + require.Len(t, deposits, 2) + deposit = getQueriedDeposit(t, ctx, cdc, querier, proposalID2, addrs[0]) + require.True(t, deposit.Equals(deposits[0])) + deposit = getQueriedDeposit(t, ctx, cdc, querier, proposalID2, addrs[1]) + require.True(t, deposit.Equals(deposits[1])) + + // check deposits on proposal3 match individual deposits + deposits = getQueriedDeposits(t, ctx, cdc, querier, proposalID3) + require.Len(t, deposits, 1) + deposit = getQueriedDeposit(t, ctx, cdc, querier, proposalID3, addrs[1]) + require.Equal(t, deposit, deposits[0]) + + // Only proposal #1 should be in Deposit Period + proposals := getQueriedProposals(t, ctx, cdc, querier, nil, nil, StatusDepositPeriod, 0) + require.Len(t, proposals, 1) + require.Equal(t, proposalID1, proposals[0].GetProposalID()) + // Only proposals #2 and #3 should be in Voting Period + proposals = getQueriedProposals(t, ctx, cdc, querier, nil, nil, StatusVotingPeriod, 0) + require.Len(t, proposals, 2) + require.Equal(t, proposalID2, proposals[0].GetProposalID()) + require.Equal(t, proposalID3, proposals[1].GetProposalID()) + + // Addrs[0] votes on proposals #2 & #3 + handler(ctx, NewMsgVote(addrs[0], proposalID2, OptionYes)) + handler(ctx, NewMsgVote(addrs[0], proposalID3, OptionYes)) + + // Addrs[1] votes on proposal #3 + handler(ctx, NewMsgVote(addrs[1], proposalID3, OptionYes)) + + // Test query voted by addrs[0] + proposals = getQueriedProposals(t, ctx, cdc, querier, nil, addrs[0], StatusNil, 0) + require.Equal(t, proposalID2, (proposals[0]).GetProposalID()) + require.Equal(t, proposalID3, (proposals[1]).GetProposalID()) + + // Test query votes on Proposal 2 + votes := getQueriedVotes(t, ctx, cdc, querier, proposalID2) + require.Len(t, votes, 1) + require.Equal(t, addrs[0], votes[0].Voter) + vote := getQueriedVote(t, ctx, cdc, querier, proposalID2, addrs[0]) + require.Equal(t, vote, votes[0]) + + // Test query votes on Proposal 3 + votes = getQueriedVotes(t, ctx, cdc, querier, proposalID3) + require.Len(t, votes, 2) + require.True(t, addrs[0].String() == votes[0].Voter.String()) + require.True(t, addrs[1].String() == votes[0].Voter.String()) + + // Test proposals queries with filters + + // Test query all proposals + proposals = getQueriedProposals(t, ctx, cdc, querier, nil, nil, StatusNil, 0) + require.Equal(t, proposalID1, (proposals[0]).GetProposalID()) + require.Equal(t, proposalID2, (proposals[1]).GetProposalID()) + require.Equal(t, proposalID3, (proposals[2]).GetProposalID()) + + // Test query voted by addrs[1] + proposals = getQueriedProposals(t, ctx, cdc, querier, nil, addrs[1], StatusNil, 0) + require.Equal(t, proposalID3, (proposals[0]).GetProposalID()) + + // Test query deposited by addrs[0] + proposals = getQueriedProposals(t, ctx, cdc, querier, addrs[0], nil, StatusNil, 0) + require.Equal(t, proposalID1, (proposals[0]).GetProposalID()) + + // Test query deposited by addr2 + proposals = getQueriedProposals(t, ctx, cdc, querier, addrs[1], nil, StatusNil, 0) + require.Equal(t, proposalID2, (proposals[0]).GetProposalID()) + require.Equal(t, proposalID3, (proposals[1]).GetProposalID()) + + // Test query voted AND deposited by addr1 + proposals = getQueriedProposals(t, ctx, cdc, querier, addrs[0], addrs[0], StatusNil, 0) + require.Equal(t, proposalID2, (proposals[0]).GetProposalID()) + + // Test Tally Query + tally := getQueriedTally(t, ctx, cdc, querier, proposalID2) + require.True(t, !tally.Equals(EmptyTallyResult())) +} diff --git a/x/gov/test_common.go b/x/gov/test_common.go index 4e0ffd51af..a0bc80fc2e 100644 --- a/x/gov/test_common.go +++ b/x/gov/test_common.go @@ -39,6 +39,7 @@ func getMockApp(t *testing.T, numGenAccs int) (*mock.App, Keeper, stake.Keeper, keeper := NewKeeper(mapp.Cdc, keyGov, pk, pk.Subspace("testgov"), ck, sk, DefaultCodespace) mapp.Router().AddRoute("gov", NewHandler(keeper)) + mapp.QueryRouter().AddRoute("gov", NewQuerier(keeper)) mapp.SetEndBlocker(getEndBlocker(keeper)) mapp.SetInitChainer(getInitChainer(mapp, keeper, sk)) diff --git a/x/mock/app.go b/x/mock/app.go index 627434a513..0b7b0ae16b 100644 --- a/x/mock/app.go +++ b/x/mock/app.go @@ -1,9 +1,11 @@ package mock import ( + "bytes" "fmt" "math/rand" "os" + "sort" bam "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/codec" @@ -109,23 +111,64 @@ func (app *App) InitChainer(ctx sdk.Context, _ abci.RequestInitChain) abci.Respo return abci.ResponseInitChain{} } +// Type that combines an Address with the privKey and pubKey to that address +type AddrKeys struct { + Address sdk.AccAddress + PubKey crypto.PubKey + PrivKey crypto.PrivKey +} + +// implement `Interface` in sort package. +type AddrKeysSlice []AddrKeys + +func (b AddrKeysSlice) Len() int { + return len(b) +} + +// Sorts lexographically by Address +func (b AddrKeysSlice) Less(i, j int) bool { + // bytes package already implements Comparable for []byte. + switch bytes.Compare(b[i].Address.Bytes(), b[j].Address.Bytes()) { + case -1: + return true + case 0, 1: + return false + default: + panic("not fail-able with `bytes.Comparable` bounded [-1, 1].") + } +} + +func (b AddrKeysSlice) Swap(i, j int) { + b[j], b[i] = b[i], b[j] +} + // CreateGenAccounts generates genesis accounts loaded with coins, and returns // their addresses, pubkeys, and privkeys. func CreateGenAccounts(numAccs int, genCoins sdk.Coins) (genAccs []auth.Account, addrs []sdk.AccAddress, pubKeys []crypto.PubKey, privKeys []crypto.PrivKey) { + addrKeysSlice := AddrKeysSlice{} + for i := 0; i < numAccs; i++ { privKey := ed25519.GenPrivKey() pubKey := privKey.PubKey() addr := sdk.AccAddress(pubKey.Address()) - genAcc := &auth.BaseAccount{ + addrKeysSlice = append(addrKeysSlice, AddrKeys{ Address: addr, - Coins: genCoins, - } + PubKey: pubKey, + PrivKey: privKey, + }) + } - genAccs = append(genAccs, genAcc) - privKeys = append(privKeys, privKey) - pubKeys = append(pubKeys, pubKey) - addrs = append(addrs, addr) + sort.Sort(addrKeysSlice) + + for i := range addrKeysSlice { + addrs = append(addrs, addrKeysSlice[i].Address) + pubKeys = append(pubKeys, addrKeysSlice[i].PubKey) + privKeys = append(privKeys, addrKeysSlice[i].PrivKey) + genAccs = append(genAccs, &auth.BaseAccount{ + Address: addrKeysSlice[i].Address, + Coins: genCoins, + }) } return diff --git a/x/stake/client/cli/query.go b/x/stake/client/cli/query.go index 28d09df8ef..24e4499960 100644 --- a/x/stake/client/cli/query.go +++ b/x/stake/client/cli/query.go @@ -127,9 +127,7 @@ func GetCmdQueryValidatorUnbondingDelegations(queryRoute string, cdc *codec.Code } cliCtx := context.NewCLIContext().WithCodec(cdc) - params := stake.QueryValidatorParams{ - ValidatorAddr: valAddr, - } + params := stake.NewQueryValidatorParams(valAddr) bz, err := cdc.MarshalJSON(params) if err != nil { @@ -164,9 +162,7 @@ func GetCmdQueryValidatorRedelegations(queryRoute string, cdc *codec.Codec) *cob } cliCtx := context.NewCLIContext().WithCodec(cdc) - params := stake.QueryValidatorParams{ - ValidatorAddr: valAddr, - } + params := stake.NewQueryValidatorParams(valAddr) bz, err := cdc.MarshalJSON(params) if err != nil { @@ -290,6 +286,41 @@ func GetCmdQueryDelegations(storeName string, cdc *codec.Codec) *cobra.Command { return cmd } +// GetCmdQueryValidatorDelegations implements the command to query all the +// delegations to a specific validator. +func GetCmdQueryValidatorDelegations(queryRoute string, cdc *codec.Codec) *cobra.Command { + cmd := &cobra.Command{ + Use: "delegations-to [validator-addr]", + Short: "Query all delegations made to one validator", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + validatorAddr, err := sdk.ValAddressFromBech32(args[0]) + if err != nil { + return err + } + + params := stake.NewQueryValidatorParams(validatorAddr) + + bz, err := cdc.MarshalJSON(params) + if err != nil { + return err + } + + cliCtx := context.NewCLIContext().WithCodec(cdc) + + res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/validatorDelegations", queryRoute), bz) + if err != nil { + return err + } + + fmt.Println(string(res)) + return nil + }, + } + + return cmd +} + // GetCmdQueryUnbondingDelegation implements the command to query a single // unbonding-delegation record. func GetCmdQueryUnbondingDelegation(storeName string, cdc *codec.Codec) *cobra.Command { diff --git a/x/stake/client/rest/query.go b/x/stake/client/rest/query.go index 935c0229f6..8c669ee662 100644 --- a/x/stake/client/rest/query.go +++ b/x/stake/client/rest/query.go @@ -78,6 +78,12 @@ func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Co validatorHandlerFn(cliCtx, cdc), ).Methods("GET") + // Get all delegations to a validator + r.HandleFunc( + "/stake/validators/{validatorAddr}/delegations", + validatorDelegationsHandlerFn(cliCtx, cdc), + ).Methods("GET") + // Get all unbonding delegations from a validator r.HandleFunc( "/stake/validators/{validatorAddr}/unbonding_delegations", @@ -227,6 +233,11 @@ func validatorHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Handle return queryValidator(cliCtx, cdc, "custom/stake/validator") } +// HTTP request handler to query all unbonding delegations from a validator +func validatorDelegationsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { + return queryValidator(cliCtx, cdc, "custom/stake/validatorDelegations") +} + // HTTP request handler to query all unbonding delegations from a validator func validatorUnbondingDelegationsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { return queryValidator(cliCtx, cdc, "custom/stake/validatorUnbondingDelegations") diff --git a/x/stake/client/rest/utils.go b/x/stake/client/rest/utils.go index bb986c3147..7f6edc1938 100644 --- a/x/stake/client/rest/utils.go +++ b/x/stake/client/rest/utils.go @@ -62,10 +62,7 @@ func queryBonds(cliCtx context.CLIContext, cdc *codec.Codec, endpoint string) ht return } - params := stake.QueryBondsParams{ - DelegatorAddr: delegatorAddr, - ValidatorAddr: validatorAddr, - } + params := stake.NewQueryBondsParams(delegatorAddr, validatorAddr) bz, err := cdc.MarshalJSON(params) if err != nil { @@ -93,9 +90,7 @@ func queryDelegator(cliCtx context.CLIContext, cdc *codec.Codec, endpoint string return } - params := stake.QueryDelegatorParams{ - DelegatorAddr: delegatorAddr, - } + params := stake.NewQueryDelegatorParams(delegatorAddr) bz, err := cdc.MarshalJSON(params) if err != nil { @@ -123,9 +118,7 @@ func queryValidator(cliCtx context.CLIContext, cdc *codec.Codec, endpoint string return } - params := stake.QueryValidatorParams{ - ValidatorAddr: validatorAddr, - } + params := stake.NewQueryValidatorParams(validatorAddr) bz, err := cdc.MarshalJSON(params) if err != nil { diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index d535419e13..32f63f5ed4 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -37,6 +37,21 @@ func (k Keeper) GetAllDelegations(ctx sdk.Context) (delegations []types.Delegati return delegations } +// return all delegations to a specific validator. Useful for querier. +func (k Keeper) GetValidatorDelegations(ctx sdk.Context, valAddr sdk.ValAddress) (delegations []types.Delegation) { + store := ctx.KVStore(k.storeKey) + iterator := sdk.KVStorePrefixIterator(store, DelegationKey) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Key(), iterator.Value()) + if delegation.GetValidatorAddr().Equals(valAddr) { + delegations = append(delegations, delegation) + } + } + return delegations +} + // return a given amount of all the delegations from a delegator func (k Keeper) GetDelegatorDelegations(ctx sdk.Context, delegator sdk.AccAddress, maxRetrieve uint16) (delegations []types.Delegation) { diff --git a/x/stake/keeper/delegation_test.go b/x/stake/keeper/delegation_test.go index 23dcbc1a6d..3fa641fd2a 100644 --- a/x/stake/keeper/delegation_test.go +++ b/x/stake/keeper/delegation_test.go @@ -105,6 +105,9 @@ func TestDelegation(t *testing.T) { resVal, err = keeper.GetDelegatorValidator(ctx, addrDels[1], addrVals[i]) require.Nil(t, err) require.Equal(t, addrVals[i], resVal.GetOperator()) + + resDels := keeper.GetValidatorDelegations(ctx, addrVals[i]) + require.Len(t, resDels, 2) } // delete a record diff --git a/x/stake/querier/queryable.go b/x/stake/querier/queryable.go index bacc8d6aec..13ff97ef3f 100644 --- a/x/stake/querier/queryable.go +++ b/x/stake/querier/queryable.go @@ -15,6 +15,7 @@ const ( QueryDelegatorDelegations = "delegatorDelegations" QueryDelegatorUnbondingDelegations = "delegatorUnbondingDelegations" QueryDelegatorRedelegations = "delegatorRedelegations" + QueryValidatorDelegations = "validatorDelegations" QueryValidatorUnbondingDelegations = "validatorUnbondingDelegations" QueryValidatorRedelegations = "validatorRedelegations" QueryDelegator = "delegator" @@ -34,6 +35,8 @@ func NewQuerier(k keep.Keeper, cdc *codec.Codec) sdk.Querier { return queryValidators(ctx, cdc, k) case QueryValidator: return queryValidator(ctx, cdc, req, k) + case QueryValidatorDelegations: + return queryValidatorDelegations(ctx, cdc, req, k) case QueryValidatorUnbondingDelegations: return queryValidatorUnbondingDelegations(ctx, cdc, req, k) case QueryValidatorRedelegations: @@ -73,6 +76,7 @@ type QueryDelegatorParams struct { // defines the params for the following queries: // - 'custom/stake/validator' +// - 'custom/stake/validatorDelegations' // - 'custom/stake/validatorUnbondingDelegations' // - 'custom/stake/validatorRedelegations' type QueryValidatorParams struct { @@ -88,6 +92,28 @@ type QueryBondsParams struct { ValidatorAddr sdk.ValAddress } +// creates a new QueryDelegatorParams +func NewQueryDelegatorParams(delegatorAddr sdk.AccAddress) QueryDelegatorParams { + return QueryDelegatorParams{ + DelegatorAddr: delegatorAddr, + } +} + +// creates a new QueryValidatorParams +func NewQueryValidatorParams(validatorAddr sdk.ValAddress) QueryValidatorParams { + return QueryValidatorParams{ + ValidatorAddr: validatorAddr, + } +} + +// creates a new QueryBondsParams +func NewQueryBondsParams(delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) QueryBondsParams { + return QueryBondsParams{ + DelegatorAddr: delegatorAddr, + ValidatorAddr: validatorAddr, + } +} + func queryValidators(ctx sdk.Context, cdc *codec.Codec, k keep.Keeper) (res []byte, err sdk.Error) { stakeParams := k.GetParams(ctx) validators := k.GetValidators(ctx, stakeParams.MaxValidators) @@ -119,6 +145,23 @@ func queryValidator(ctx sdk.Context, cdc *codec.Codec, req abci.RequestQuery, k return res, nil } +func queryValidatorDelegations(ctx sdk.Context, cdc *codec.Codec, 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("") + } + + delegations := k.GetValidatorDelegations(ctx, params.ValidatorAddr) + + res, errRes = codec.MarshalJSONIndent(cdc, delegations) + if errRes != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", errRes.Error())) + } + return res, nil +} + func queryValidatorUnbondingDelegations(ctx sdk.Context, cdc *codec.Codec, req abci.RequestQuery, k keep.Keeper) (res []byte, err sdk.Error) { var params QueryValidatorParams diff --git a/x/stake/querier/queryable_test.go b/x/stake/querier/queryable_test.go index 925eae63f7..eda5c69525 100644 --- a/x/stake/querier/queryable_test.go +++ b/x/stake/querier/queryable_test.go @@ -17,25 +17,6 @@ var ( 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 TestNewQuerier(t *testing.T) { cdc := codec.New() ctx, _, keeper := keep.CreateTestInput(t, false, 1000) @@ -72,7 +53,7 @@ func TestNewQuerier(t *testing.T) { _, err = querier(ctx, []string{"parameters"}, query) require.Nil(t, err) - queryValParams := newTestValidatorQuery(addrVal1) + queryValParams := NewQueryValidatorParams(addrVal1) bz, errRes := cdc.MarshalJSON(queryValParams) require.Nil(t, errRes) @@ -82,13 +63,16 @@ func TestNewQuerier(t *testing.T) { _, err = querier(ctx, []string{"validator"}, query) require.Nil(t, err) + _, err = querier(ctx, []string{"validatorDelegations"}, query) + require.Nil(t, err) + _, err = querier(ctx, []string{"validatorUnbondingDelegations"}, query) require.Nil(t, err) _, err = querier(ctx, []string{"validatorRedelegations"}, query) require.Nil(t, err) - queryDelParams := newTestDelegatorQuery(addrAcc2) + queryDelParams := NewQueryDelegatorParams(addrAcc2) bz, errRes = cdc.MarshalJSON(queryDelParams) require.Nil(t, errRes) @@ -160,7 +144,7 @@ func TestQueryValidators(t *testing.T) { require.ElementsMatch(t, queriedValidators, validatorsResp) // Query each validator - queryParams := newTestValidatorQuery(addrVal1) + queryParams := NewQueryValidatorParams(addrVal1) bz, errRes := cdc.MarshalJSON(queryParams) require.Nil(t, errRes) @@ -189,13 +173,18 @@ func TestQueryDelegation(t *testing.T) { pool := keeper.GetPool(ctx) keeper.SetValidatorByPowerIndex(ctx, val1, pool) + val2 := types.NewValidator(addrVal2, pk2, types.Description{}) + keeper.SetValidator(ctx, val2) + pool = keeper.GetPool(ctx) + keeper.SetValidatorByPowerIndex(ctx, val2, pool) + keeper.Delegate(ctx, addrAcc2, sdk.NewCoin(types.DefaultBondDenom, sdk.NewInt(20)), val1, true) // apply TM updates keeper.ApplyAndReturnValidatorSetUpdates(ctx) // Query Delegator bonded validators - queryParams := newTestDelegatorQuery(addrAcc2) + queryParams := NewQueryDelegatorParams(addrAcc2) bz, errRes := cdc.MarshalJSON(queryParams) require.Nil(t, errRes) @@ -223,7 +212,7 @@ func TestQueryDelegation(t *testing.T) { require.NotNil(t, err) // Query bonded validator - queryBondParams := newTestBondQuery(addrAcc2, addrVal1) + queryBondParams := NewQueryBondsParams(addrAcc2, addrVal1) bz, errRes = cdc.MarshalJSON(queryBondParams) require.Nil(t, errRes) @@ -288,9 +277,32 @@ func TestQueryDelegation(t *testing.T) { _, err = queryDelegation(ctx, cdc, query, keeper) require.NotNil(t, err) + // Query validator delegations + + bz, errRes = cdc.MarshalJSON(NewQueryValidatorParams(addrVal1)) + require.Nil(t, errRes) + + query = abci.RequestQuery{ + Path: "custom/stake/validatorDelegations", + Data: bz, + } + + res, err = queryValidatorDelegations(ctx, cdc, query, keeper) + require.Nil(t, err) + + var delegationsRes []types.Delegation + errRes = cdc.UnmarshalJSON(res, &delegationsRes) + require.Nil(t, errRes) + + require.Equal(t, delegationsRes[0], delegation) + // Query unbonging delegation keeper.BeginUnbonding(ctx, addrAcc2, val1.OperatorAddr, sdk.NewDec(10)) + queryBondParams = NewQueryBondsParams(addrAcc2, addrVal1) + bz, errRes = cdc.MarshalJSON(queryBondParams) + require.Nil(t, errRes) + query = abci.RequestQuery{ Path: "/custom/stake/unbondingDelegation", Data: bz, @@ -356,7 +368,7 @@ func TestQueryRedelegations(t *testing.T) { require.True(t, found) // delegator redelegations - queryDelegatorParams := newTestDelegatorQuery(addrAcc2) + queryDelegatorParams := NewQueryDelegatorParams(addrAcc2) bz, errRes := cdc.MarshalJSON(queryDelegatorParams) require.Nil(t, errRes) @@ -375,7 +387,7 @@ func TestQueryRedelegations(t *testing.T) { require.Equal(t, redelegation, redsRes[0]) // validator redelegations - queryValidatorParams := newTestValidatorQuery(val1.GetOperator()) + queryValidatorParams := NewQueryValidatorParams(val1.GetOperator()) bz, errRes = cdc.MarshalJSON(queryValidatorParams) require.Nil(t, errRes) diff --git a/x/stake/stake.go b/x/stake/stake.go index 87087e59c0..a922d9d72e 100644 --- a/x/stake/stake.go +++ b/x/stake/stake.go @@ -84,7 +84,10 @@ var ( NewMsgBeginUnbonding = types.NewMsgBeginUnbonding NewMsgBeginRedelegate = types.NewMsgBeginRedelegate - NewQuerier = querier.NewQuerier + NewQuerier = querier.NewQuerier + NewQueryDelegatorParams = querier.NewQueryDelegatorParams + NewQueryValidatorParams = querier.NewQueryValidatorParams + NewQueryBondsParams = querier.NewQueryBondsParams ) const (