diff --git a/PENDING.md b/PENDING.md index 3c56e9234427..cc72812a0f27 100644 --- a/PENDING.md +++ b/PENDING.md @@ -3,7 +3,8 @@ BREAKING CHANGES * Gaia REST API (`gaiacli advanced rest-server`) - * [\#3056](https://github.com/cosmos/cosmos-sdk/pull/3056) `generate_only` and `simulate` have moved from query arguments to POST requests body. + * [gaia-lite] [\#2191](https://github.com/cosmos/cosmos-sdk/issues/2191) Split `POST /stake/delegators/{delegatorAddr}/delegations` into `POST /stake/delegators/{delegatorAddr}/delegations`, `POST /stake/delegators/{delegatorAddr}/unbonding_delegations` and `POST /stake/delegators/{delegatorAddr}/redelegations` + * [gaia-lite] [\#3056](https://github.com/cosmos/cosmos-sdk/pull/3056) `generate_only` and `simulate` have moved from query arguments to POST requests body. * Gaia CLI (`gaiacli`) diff --git a/client/context/context.go b/client/context/context.go index df30374de991..9fac7c27a872 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -47,7 +47,7 @@ type CLIContext struct { JSON bool PrintResponse bool Verifier tmlite.Verifier - DryRun bool + Simulate bool GenerateOnly bool fromAddress types.AccAddress fromName string @@ -85,7 +85,7 @@ func NewCLIContext() CLIContext { JSON: viper.GetBool(client.FlagJson), PrintResponse: viper.GetBool(client.FlagPrintResponse), Verifier: verifier, - DryRun: viper.GetBool(client.FlagDryRun), + Simulate: viper.GetBool(client.FlagDryRun), GenerateOnly: viper.GetBool(client.FlagGenerateOnly), fromAddress: fromAddress, fromName: fromName, @@ -244,3 +244,15 @@ func (ctx CLIContext) WithVerifier(verifier tmlite.Verifier) CLIContext { ctx.Verifier = verifier return ctx } + +// WithGenerateOnly returns a copy of the context with updated GenerateOnly value +func (ctx CLIContext) WithGenerateOnly(generateOnly bool) CLIContext { + ctx.GenerateOnly = generateOnly + return ctx +} + +// WithSimulation returns a copy of the context with updated Simulate value +func (ctx CLIContext) WithSimulation(simulate bool) CLIContext { + ctx.Simulate = simulate + return ctx +} diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 34f8edbc90f2..bc64c23214cb 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -342,7 +342,7 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { acc := getAccount(t, port, addr) // generate TX - res, body, _ := doSendWithGas(t, port, seed, name, password, addr, "simulate", 0, false, true) + res, body, _ := doSendWithGas(t, port, seed, name, "", addr, "simulate", 0, false, true) require.Equal(t, http.StatusOK, res.StatusCode, body) var msg auth.StdTx require.Nil(t, cdc.UnmarshalJSON([]byte(body), &msg)) @@ -1176,15 +1176,9 @@ func doDelegate(t *testing.T, port, seed, name, password string, chainID := viper.GetString(client.FlagChainID) jsonStr := []byte(fmt.Sprintf(`{ - "delegations": [ - { - "delegator_addr": "%s", - "validator_addr": "%s", - "delegation": { "denom": "%s", "amount": "%d" } - } - ], - "begin_unbondings": [], - "begin_redelegates": [], + "delegator_addr": "%s", + "validator_addr": "%s", + "delegation": { "denom": "%s", "amount": "%d" }, "base_req": { "name": "%s", "password": "%s", @@ -1197,11 +1191,10 @@ func doDelegate(t *testing.T, port, seed, name, password string, res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delAddr), jsonStr) require.Equal(t, http.StatusOK, res.StatusCode, body) - var results []ctypes.ResultBroadcastTxCommit - err := cdc.UnmarshalJSON([]byte(body), &results) + err := cdc.UnmarshalJSON([]byte(body), &resultTx) require.Nil(t, err) - return results[0] + return } func doBeginUnbonding(t *testing.T, port, seed, name, password string, @@ -1213,15 +1206,9 @@ func doBeginUnbonding(t *testing.T, port, seed, name, password string, chainID := viper.GetString(client.FlagChainID) jsonStr := []byte(fmt.Sprintf(`{ - "delegations": [], - "begin_unbondings": [ - { - "delegator_addr": "%s", - "validator_addr": "%s", - "shares": "%d" - } - ], - "begin_redelegates": [], + "delegator_addr": "%s", + "validator_addr": "%s", + "shares": "%d", "base_req": { "name": "%s", "password": "%s", @@ -1231,14 +1218,13 @@ func doBeginUnbonding(t *testing.T, port, seed, name, password string, } }`, delAddr, valAddr, amount, name, password, chainID, accnum, sequence)) - res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delAddr), jsonStr) + res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/unbonding_delegations", delAddr), jsonStr) require.Equal(t, http.StatusOK, res.StatusCode, body) - var results []ctypes.ResultBroadcastTxCommit - err := cdc.UnmarshalJSON([]byte(body), &results) + err := cdc.UnmarshalJSON([]byte(body), &resultTx) require.Nil(t, err) - return results[0] + return } func doBeginRedelegation(t *testing.T, port, seed, name, password string, @@ -1251,16 +1237,10 @@ func doBeginRedelegation(t *testing.T, port, seed, name, password string, chainID := viper.GetString(client.FlagChainID) jsonStr := []byte(fmt.Sprintf(`{ - "delegations": [], - "begin_unbondings": [], - "begin_redelegates": [ - { - "delegator_addr": "%s", - "validator_src_addr": "%s", - "validator_dst_addr": "%s", - "shares": "%d" - } - ], + "delegator_addr": "%s", + "validator_src_addr": "%s", + "validator_dst_addr": "%s", + "shares": "%d", "base_req": { "name": "%s", "password": "%s", @@ -1270,14 +1250,13 @@ func doBeginRedelegation(t *testing.T, port, seed, name, password string, } }`, delAddr, valSrcAddr, valDstAddr, amount, name, password, chainID, accnum, sequence)) - res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/delegations", delAddr), jsonStr) + res, body := Request(t, port, "POST", fmt.Sprintf("/stake/delegators/%s/redelegations", delAddr), jsonStr) require.Equal(t, http.StatusOK, res.StatusCode, body) - var results []ctypes.ResultBroadcastTxCommit - err := cdc.UnmarshalJSON([]byte(body), &results) + err := cdc.UnmarshalJSON([]byte(body), &resultTx) require.Nil(t, err) - return results[0] + return } func getValidators(t *testing.T, port string) []stake.Validator { diff --git a/client/lcd/swagger-ui/swagger.yaml b/client/lcd/swagger-ui/swagger.yaml index 0ecbe505fedf..a76bdddd68e3 100644 --- a/client/lcd/swagger-ui/swagger.yaml +++ b/client/lcd/swagger-ui/swagger.yaml @@ -625,6 +625,23 @@ paths: description: Bech32 AccAddress of Delegator required: true type: string + get: + summary: Get all delegations from a delegator + tags: + - ICS21 + produces: + - application/json + responses: + 200: + description: OK + schema: + type: array + items: + $ref: "#/definitions/Delegation" + 400: + description: Invalid delegator address + 500: + description: Internal Server Error post: summary: Submit delegation parameters: @@ -635,44 +652,13 @@ paths: type: object properties: base_req: - "$ref": "#/definitions/BaseReq" - delegations: - type: array - items: - type: object - properties: - delegator_addr: - $ref: "#/definitions/Address" - validator_addr: - $ref: "#/definitions/ValidatorAddress" - delegation: - $ref: "#/definitions/Coin" - begin_unbondings: - type: array - items: - type: object - properties: - delegator_addr: - $ref: "#/definitions/Address" - validator_addr: - $ref: "#/definitions/ValidatorAddress" - shares: - type: string - example: "100" - begin_redelegates: - type: array - items: - type: object - properties: - delegator_addr: - $ref: "#/definitions/Address" - validator_src_addr: - $ref: "#/definitions/ValidatorAddress" - validator_dst_addr: - $ref: "#/definitions/ValidatorAddress" - shares: - type: string - example: "100" + $ref: "#/definitions/BaseReq" + delegator_addr: + $ref: "#/definitions/Address" + validator_addr: + $ref: "#/definitions/ValidatorAddress" + delegation: + $ref: "#/definitions/Coin" tags: - ICS21 consumes: @@ -685,13 +671,25 @@ paths: schema: $ref: "#/definitions/BroadcastTxCommitResult" 400: - description: Invalid delegator address or delegation body + description: Invalid delegator address or delegation request body 401: description: Key password is wrong 500: description: Internal Server Error + /stake/delegators/{delegatorAddr}/delegations/{validatorAddr}: + parameters: + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string get: - summary: Get all delegations from a delegator + summary: Query the current delegation between a delegator and a validator tags: - ICS21 produces: @@ -700,12 +698,9 @@ paths: 200: description: OK schema: - type: array - items: - type: object - "$ref": "#/definitions/Delegation" + $ref: "#/definitions/Delegation" 400: - description: Invalid delegator address + description: Invalid delegator address or validator address 500: description: Internal Server Error /stake/delegators/{delegatorAddr}/unbonding_delegations: @@ -727,21 +722,70 @@ paths: schema: type: array items: - type: object - "$ref": "#/definitions/UnbondingDelegation" + $ref: "#/definitions/UnbondingDelegation" 400: description: Invalid delegator address 500: description: Internal Server Error - /stake/delegators/{delegatorAddr}/redelegations: + post: + summary: Submit an unbonding delegation + parameters: + - in: query + name: simulate + description: if true, ignore the gas field and perform a simulation of a transaction, but don't broadcast it + required: false + type: boolean + - in: query + name: generate_only + description: if true, build an unsigned transaction and write it back + required: false + type: boolean + - in: body + name: delegation + description: The password of the account to remove from the KMS + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + delegator_addr: + $ref: "#/definitions/Address" + validator_addr: + $ref: "#/definitions/ValidatorAddress" + shares: + type: string + example: "100" + tags: + - ICS21 + consumes: + - application/json + produces: + - application/json + responses: + 200: + description: OK + schema: + $ref: "#/definitions/BroadcastTxCommitResult" + 400: + description: Invalid delegator address or unbonding delegation request body + 401: + description: Key password is wrong + 500: + description: Internal Server Error + /stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}: parameters: - in: path name: delegatorAddr description: Bech32 AccAddress of Delegator required: true type: string + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string get: - summary: Get all redelegations from a delegator + summary: Query all unbonding delegations between a delegator and a validator tags: - ICS21 produces: @@ -752,13 +796,12 @@ paths: schema: type: array items: - type: object - "$ref": "#/definitions/Redelegation" + $ref: "#/definitions/UnbondingDelegation" 400: - description: Invalid delegator address + description: Invalid delegator address or validator address 500: description: Internal Server Error - /stake/delegators/{delegatorAddr}/validators: + /stake/delegators/{delegatorAddr}/redelegations: parameters: - in: path name: delegatorAddr @@ -766,7 +809,7 @@ paths: required: true type: string get: - summary: Query all validators that a delegator is bonded to + summary: Get all redelegations from a delegator tags: - ICS21 produces: @@ -777,39 +820,59 @@ paths: schema: type: array items: - $ref: "#/definitions/Validator" + $ref: "#/definitions/Redelegation" 400: description: Invalid delegator address 500: description: Internal Server Error - /stake/delegators/{delegatorAddr}/validators/{validatorAddr}: - parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string - - in: path - name: validatorAddr - description: Bech32 ValAddress of Delegator - required: true - type: string - get: - summary: Query a validator that a delegator is bonded to + post: + summary: Submit a redelegation + parameters: + - in: query + name: simulate + description: if true, ignore the gas field and perform a simulation of a transaction, but don't broadcast it + required: false + type: boolean + - in: query + name: generate_only + description: if true, build an unsigned transaction and write it back + required: false + type: boolean + - in: body + name: delegation + description: The password of the account to remove from the KMS + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + delegator_addr: + $ref: "#/definitions/Address" + validator_src_addr: + $ref: "#/definitions/ValidatorAddress" + validator_dst_addr: + $ref: "#/definitions/ValidatorAddress" + shares: + type: string + example: "100" tags: - - ICS21 + - ICS21 + consumes: + - application/json produces: - - application/json + - application/json responses: 200: description: OK schema: - $ref: "#/definitions/Validator" + $ref: "#/definitions/BroadcastTxCommitResult" 400: - description: Invalid delegator address or validator address + description: Invalid delegator address or redelegation request body + 401: + description: Key password is wrong 500: description: Internal Server Error - /stake/delegators/{delegatorAddr}/txs: + /stake/delegators/{delegatorAddr}/validators: parameters: - in: path name: delegatorAddr @@ -817,7 +880,7 @@ paths: required: true type: string get: - summary: Get all staking txs (i.e msgs) from a delegator + summary: Query all validators that a delegator is bonded to tags: - ICS21 produces: @@ -828,14 +891,12 @@ paths: schema: type: array items: - $ref: "#/definitions/TxQuery" - 204: - description: No staking transaction about this delegator address + $ref: "#/definitions/Validator" 400: description: Invalid delegator address 500: description: Internal Server Error - /stake/delegators/{delegatorAddr}/delegations/{validatorAddr}: + /stake/delegators/{delegatorAddr}/validators/{validatorAddr}: parameters: - in: path name: delegatorAddr @@ -844,11 +905,11 @@ paths: type: string - in: path name: validatorAddr - description: Bech32 OperatorAddress of validator + description: Bech32 ValAddress of Delegator required: true type: string get: - summary: Query the current delegation between a delegator and a validator + summary: Query a validator that a delegator is bonded to tags: - ICS21 produces: @@ -857,25 +918,20 @@ paths: 200: description: OK schema: - $ref: "#/definitions/Delegation" + $ref: "#/definitions/Validator" 400: description: Invalid delegator address or validator address 500: description: Internal Server Error - /stake/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}: + /stake/delegators/{delegatorAddr}/txs: parameters: - in: path name: delegatorAddr description: Bech32 AccAddress of Delegator required: true type: string - - in: path - name: validatorAddr - description: Bech32 OperatorAddress of validator - required: true - type: string get: - summary: Query all unbonding delegations between a delegator and a validator + summary: Get all staking txs (i.e msgs) from a delegator tags: - ICS21 produces: @@ -886,10 +942,11 @@ paths: schema: type: array items: - type: object - "$ref": "#/definitions/UnbondingDelegation" + $ref: "#/definitions/TxQuery" + 204: + description: No staking transaction about this delegator address 400: - description: Invalid delegator address or validator address + description: Invalid delegator address 500: description: Internal Server Error /stake/validators: @@ -1116,7 +1173,7 @@ paths: type: object properties: base_req: - "$ref": "#/definitions/BaseReq" + $ref: "#/definitions/BaseReq" responses: 200: description: OK @@ -1147,7 +1204,7 @@ paths: type: object properties: base_req: - "$ref": "#/definitions/BaseReq" + $ref: "#/definitions/BaseReq" title: type: string description: @@ -1156,7 +1213,7 @@ paths: type: string example: "text" proposer: - "$ref": "#/definitions/Address" + $ref: "#/definitions/Address" initial_deposit: type: array items: @@ -1165,7 +1222,7 @@ paths: 200: description: OK schema: - "$ref": "#/definitions/BroadcastTxCommitResult" + $ref: "#/definitions/BroadcastTxCommitResult" 400: description: Invalid proposal body 401: @@ -1201,53 +1258,34 @@ paths: schema: type: array items: - "$ref": "#/definitions/TextProposal" + $ref: "#/definitions/TextProposal" 400: description: Invalid query parameters 500: description: Internal Server Error - /gov/proposals/{proposalId}/deposits: - post: - summary: Deposit tokens to a proposal - description: Send transaction to deposit tokens to a proposal - consumes: - - application/json + /gov/proposals/{proposalId}: + get: + summary: Query a proposal + description: Query a proposal by id produces: - application/json tags: - ICS22 parameters: - type: string - description: proposal id name: proposalId required: true in: path - - description: '' - name: post_deposit_body - in: body - required: true - schema: - type: object - properties: - base_req: - "$ref": "#/definitions/BaseReq" - depositor: - "$ref": "#/definitions/Address" - amount: - type: array - items: - $ref: "#/definitions/Coin" responses: 200: description: OK schema: - "$ref": "#/definitions/BroadcastTxCommitResult" + $ref: "#/definitions/TextProposal" 400: - description: Invalid proposal id or deposit body - 401: - description: Key password is wrong + description: Invalid proposal id 500: description: Internal Server Error + /gov/proposals/{proposalId}/deposits: get: summary: Query deposits description: Query deposits by proposalId @@ -1266,15 +1304,16 @@ paths: schema: type: array items: - "$ref": "#/definitions/Deposit" + $ref: "#/definitions/Deposit" 400: description: Invalid proposal id 500: description: Internal Server Error - /gov/proposals/{proposalId}/tally: - get: - summary: Get a proposal's tally result at the current time - description: Gets a proposal's tally result at the current time. If the proposal is pending deposits (i.e status 'DepositPeriod') it returns an empty tally result. + post: + summary: Deposit tokens to a proposal + description: Send transaction to deposit tokens to a proposal + consumes: + - application/json produces: - application/json tags: @@ -1285,21 +1324,36 @@ paths: name: proposalId required: true in: path + - description: '' + name: post_deposit_body + in: body + required: true + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + depositor: + $ref: "#/definitions/Address" + amount: + type: array + items: + $ref: "#/definitions/Coin" responses: 200: description: OK schema: - $ref: "#/definitions/TallyResult" + $ref: "#/definitions/BroadcastTxCommitResult" 400: - description: Invalid proposal id + description: Invalid proposal id or deposit body + 401: + description: Key password is wrong 500: description: Internal Server Error - /gov/proposals/{proposalId}/votes: - post: - summary: Vote a proposal - description: Send transaction to vote a proposal - consumes: - - application/json + /gov/proposals/{proposalId}/deposits/{depositor}: + get: + summary: Query deposit + description: Query deposit by proposalId and depositor address produces: - application/json tags: @@ -1310,31 +1364,23 @@ paths: name: proposalId required: true in: path - - description: valid value of `"option"` field can be `"yes"`, `"no"`, `"no_with_veto"` and `"abstain"` - name: post_vote_body - in: body + - type: string + description: Bech32 depositor address + name: depositor required: true - schema: - type: object - properties: - base_req: - "$ref": "#/definitions/BaseReq" - voter: - "$ref": "#/definitions/Address" - option: - type: string - example: "yes" + in: path responses: 200: description: OK schema: - "$ref": "#/definitions/BroadcastTxCommitResult" + $ref: "#/definitions/Deposit" 400: - description: Invalid proposal id or vote body - 401: - description: Key password is wrong + description: Invalid proposal id or depositor address + 404: + description: Found no deposit 500: description: Internal Server Error + /gov/proposals/{proposalId}/votes: get: summary: Query voters description: Query voters information by proposalId @@ -1354,37 +1400,55 @@ paths: schema: type: array items: - "$ref": "#/definitions/Vote" + $ref: "#/definitions/Vote" 400: description: Invalid proposal id 500: description: Internal Server Error - /gov/proposals/{proposalId}: - get: - summary: Query a proposal - description: Query a proposal by id + post: + summary: Vote a proposal + description: Send transaction to vote a proposal + consumes: + - application/json produces: - application/json tags: - ICS22 parameters: - type: string + description: proposal id name: proposalId required: true in: path + - description: valid value of `"option"` field can be `"yes"`, `"no"`, `"no_with_veto"` and `"abstain"` + name: post_vote_body + in: body + required: true + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + voter: + $ref: "#/definitions/Address" + option: + type: string + example: "yes" responses: 200: description: OK schema: - "$ref": "#/definitions/TextProposal" + $ref: "#/definitions/BroadcastTxCommitResult" 400: - description: Invalid proposal id + description: Invalid proposal id or vote body + 401: + description: Key password is wrong 500: description: Internal Server Error - /gov/proposals/{proposalId}/deposits/{depositor}: + /gov/proposals/{proposalId}/votes/{voter}: get: - summary: Query deposit - description: Query deposit by proposalId and depositor address + summary: Query vote + description: Query vote information by proposal Id and voter address produces: - application/json tags: @@ -1396,25 +1460,25 @@ paths: required: true in: path - type: string - description: Bech32 depositor address - name: depositor + description: Bech32 voter address + name: voter required: true in: path responses: 200: description: OK schema: - $ref: "#/definitions/Deposit" + $ref: "#/definitions/Vote" 400: - description: Invalid proposal id or depositor address + description: Invalid proposal id or voter address 404: - description: Found no deposit + description: Found no vote 500: description: Internal Server Error - /gov/proposals/{proposalId}/votes/{voter}: + /gov/proposals/{proposalId}/tally: get: - summary: Query vote - description: Query vote information by proposal Id and voter address + summary: Get a proposal's tally result at the current time + description: Gets a proposal's tally result at the current time. If the proposal is pending deposits (i.e status 'DepositPeriod') it returns an empty tally result. produces: - application/json tags: @@ -1425,20 +1489,13 @@ paths: name: proposalId required: true in: path - - type: string - description: Bech32 voter address - name: voter - required: true - in: path responses: 200: description: OK schema: - $ref: "#/definitions/Vote" + $ref: "#/definitions/TallyResult" 400: - description: Invalid proposal id or voter address - 404: - description: Found no vote + description: Invalid proposal id 500: description: Internal Server Error /gov/parameters/deposit: @@ -1538,7 +1595,7 @@ definitions: tags: type: array items: - "$ref": "#/definitions/KVPair" + $ref: "#/definitions/KVPair" example: code: 0 data: data @@ -1567,7 +1624,7 @@ definitions: tags: type: array items: - "$ref": "#/definitions/KVPair" + $ref: "#/definitions/KVPair" example: code: 5 data: data @@ -1868,7 +1925,7 @@ definitions: total_deposit: type: array items: - "$ref": "#/definitions/Coin" + $ref: "#/definitions/Coin" voting_start_time: type: string Deposit: @@ -1877,11 +1934,11 @@ definitions: amount: type: array items: - "$ref": "#/definitions/Coin" + $ref: "#/definitions/Coin" proposal_id: type: integer depositor: - "$ref": "#/definitions/Address" + $ref: "#/definitions/Address" TallyResult: type: object properties: diff --git a/client/utils/rest.go b/client/utils/rest.go index 36ff05af8b71..5917455ee365 100644 --- a/client/utils/rest.go +++ b/client/utils/rest.go @@ -159,21 +159,21 @@ func ReadRESTReq(w http.ResponseWriter, r *http.Request, cdc *codec.Codec, req i // ValidateBasic performs basic validation of a BaseReq. If custom validation // logic is needed, the implementing request handler should perform those // checks manually. -func (br BaseReq) ValidateBasic(w http.ResponseWriter) bool { - switch { - case len(br.Name) == 0: +func (br BaseReq) ValidateBasic(w http.ResponseWriter, cliCtx context.CLIContext) bool { + if !cliCtx.GenerateOnly && !cliCtx.Simulate { + switch { + case len(br.Password) == 0: + WriteErrorResponse(w, http.StatusUnauthorized, "password required but not specified") + return false + case len(br.ChainID) == 0: + WriteErrorResponse(w, http.StatusUnauthorized, "chain-id required but not specified") + return false + } + } + if len(br.Name) == 0 { WriteErrorResponse(w, http.StatusUnauthorized, "name required but not specified") return false - - case len(br.Password) == 0: - WriteErrorResponse(w, http.StatusUnauthorized, "password required but not specified") - return false - - case len(br.ChainID) == 0: - WriteErrorResponse(w, http.StatusUnauthorized, "chainID required but not specified") - return false } - return true } diff --git a/client/utils/utils.go b/client/utils/utils.go index 08a538eec68e..6fde3ce6b5ae 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -34,14 +34,14 @@ func CompleteAndBroadcastTxCli(txBldr authtxb.TxBuilder, cliCtx context.CLIConte return err } - if txBldr.SimulateGas || cliCtx.DryRun { + if txBldr.SimulateGas || cliCtx.Simulate { txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, name, msgs) if err != nil { return err } fmt.Fprintf(os.Stderr, "estimated gas = %v\n", txBldr.Gas) } - if cliCtx.DryRun { + if cliCtx.Simulate { return nil } diff --git a/x/bank/client/rest/sendtx.go b/x/bank/client/rest/sendtx.go index 7bb2640fddfd..27cbc043c751 100644 --- a/x/bank/client/rest/sendtx.go +++ b/x/bank/client/rest/sendtx.go @@ -49,8 +49,11 @@ func SendRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIC return } + cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly) + cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate) + baseReq := req.BaseReq.Sanitize() - if !baseReq.ValidateBasic(w) { + if !baseReq.ValidateBasic(w, cliCtx) { return } diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index 53deffbbe5b5..821da0cc7de6 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -78,8 +78,11 @@ func postProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han return } + cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly) + cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate) + baseReq := req.BaseReq.Sanitize() - if !baseReq.ValidateBasic(w) { + if !baseReq.ValidateBasic(w, cliCtx) { return } @@ -123,8 +126,11 @@ func depositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerF return } + cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly) + cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate) + baseReq := req.BaseReq.Sanitize() - if !baseReq.ValidateBasic(w) { + if !baseReq.ValidateBasic(w, cliCtx) { return } @@ -162,8 +168,11 @@ func voteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc return } + cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly) + cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate) + baseReq := req.BaseReq.Sanitize() - if !baseReq.ValidateBasic(w) { + if !baseReq.ValidateBasic(w, cliCtx) { return } diff --git a/x/ibc/client/rest/transfer.go b/x/ibc/client/rest/transfer.go index 19c9219711f4..704b663e62d0 100644 --- a/x/ibc/client/rest/transfer.go +++ b/x/ibc/client/rest/transfer.go @@ -43,8 +43,11 @@ func TransferRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context. return } + cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly) + cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate) + baseReq := req.BaseReq.Sanitize() - if !baseReq.ValidateBasic(w) { + if !baseReq.ValidateBasic(w, cliCtx) { return } diff --git a/x/slashing/client/rest/tx.go b/x/slashing/client/rest/tx.go index 5f33a1210cb3..ec28f48ed690 100644 --- a/x/slashing/client/rest/tx.go +++ b/x/slashing/client/rest/tx.go @@ -38,8 +38,11 @@ func unjailRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CL return } + cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly) + cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate) + baseReq := req.BaseReq.Sanitize() - if !baseReq.ValidateBasic(w) { + if !baseReq.ValidateBasic(w, cliCtx) { return } diff --git a/x/stake/client/rest/tx.go b/x/stake/client/rest/tx.go index 5deb5b53c28e..bb2bc0e7fa78 100644 --- a/x/stake/client/rest/tx.go +++ b/x/stake/client/rest/tx.go @@ -2,81 +2,72 @@ package rest import ( "bytes" - "io/ioutil" "net/http" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" - authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" "github.com/cosmos/cosmos-sdk/x/stake" "github.com/gorilla/mux" - - ctypes "github.com/tendermint/tendermint/rpc/core/types" ) func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, kb keys.Keybase) { r.HandleFunc( "/stake/delegators/{delegatorAddr}/delegations", - delegationsRequestHandlerFn(cdc, kb, cliCtx), + postDelegationsHandlerFn(cdc, kb, cliCtx), + ).Methods("POST") + r.HandleFunc( + "/stake/delegators/{delegatorAddr}/unbonding_delegations", + postUnbondingDelegationsHandlerFn(cdc, kb, cliCtx), + ).Methods("POST") + r.HandleFunc( + "/stake/delegators/{delegatorAddr}/redelegations", + postRedelegationsHandlerFn(cdc, kb, cliCtx), ).Methods("POST") } type ( msgDelegationsInput struct { - DelegatorAddr string `json:"delegator_addr"` // in bech32 - ValidatorAddr string `json:"validator_addr"` // in bech32 - Delegation sdk.Coin `json:"delegation"` + BaseReq utils.BaseReq `json:"base_req"` + DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32 + ValidatorAddr sdk.ValAddress `json:"validator_addr"` // in bech32 + Delegation sdk.Coin `json:"delegation"` } msgBeginRedelegateInput struct { - DelegatorAddr string `json:"delegator_addr"` // in bech32 - ValidatorSrcAddr string `json:"validator_src_addr"` // in bech32 - ValidatorDstAddr string `json:"validator_dst_addr"` // in bech32 - SharesAmount string `json:"shares"` + BaseReq utils.BaseReq `json:"base_req"` + DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32 + ValidatorSrcAddr sdk.ValAddress `json:"validator_src_addr"` // in bech32 + ValidatorDstAddr sdk.ValAddress `json:"validator_dst_addr"` // in bech32 + SharesAmount sdk.Dec `json:"shares"` } msgBeginUnbondingInput struct { - DelegatorAddr string `json:"delegator_addr"` // in bech32 - ValidatorAddr string `json:"validator_addr"` // in bech32 - SharesAmount string `json:"shares"` - } - - // the request body for edit delegations - EditDelegationsReq struct { - BaseReq utils.BaseReq `json:"base_req"` - Delegations []msgDelegationsInput `json:"delegations"` - BeginUnbondings []msgBeginUnbondingInput `json:"begin_unbondings"` - BeginRedelegates []msgBeginRedelegateInput `json:"begin_redelegates"` + BaseReq utils.BaseReq `json:"base_req"` + DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32 + ValidatorAddr sdk.ValAddress `json:"validator_addr"` // in bech32 + SharesAmount sdk.Dec `json:"shares"` } ) -// TODO: Split this up into several smaller functions, and remove the above nolint -// TODO: use sdk.ValAddress instead of sdk.AccAddress for validators in messages -// TODO: Seriously consider how to refactor...do we need to make it multiple txs? -// If not, we can just use CompleteAndBroadcastTxREST. -func delegationsRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { +func postDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - var req EditDelegationsReq + var req msgDelegationsInput - body, err := ioutil.ReadAll(r.Body) + err := utils.ReadRESTReq(w, r, cdc, &req) if err != nil { utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - err = cdc.UnmarshalJSON(body, &req) - if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } + cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly) + cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate) baseReq := req.BaseReq.Sanitize() - if !baseReq.ValidateBasic(w) { + if !baseReq.ValidateBasic(w, cliCtx) { return } @@ -86,182 +77,98 @@ func delegationsRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx conte return } - // build messages - messages := make([]sdk.Msg, len(req.Delegations)+ - len(req.BeginRedelegates)+ - len(req.BeginUnbondings)) - - i := 0 - for _, msg := range req.Delegations { - delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddr) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - if !bytes.Equal(info.GetPubKey().Address(), delAddr) { - utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address") - return - } - - messages[i] = stake.MsgDelegate{ - DelegatorAddr: delAddr, - ValidatorAddr: valAddr, - Delegation: msg.Delegation, - } - - i++ + if !bytes.Equal(info.GetPubKey().Address(), req.DelegatorAddr) { + utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address") + return } - for _, msg := range req.BeginRedelegates { - delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - if !bytes.Equal(info.GetPubKey().Address(), delAddr) { - utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address") - return - } - - valSrcAddr, err := sdk.ValAddressFromBech32(msg.ValidatorSrcAddr) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - valDstAddr, err := sdk.ValAddressFromBech32(msg.ValidatorDstAddr) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - shares, err := sdk.NewDecFromStr(msg.SharesAmount) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - messages[i] = stake.MsgBeginRedelegate{ - DelegatorAddr: delAddr, - ValidatorSrcAddr: valSrcAddr, - ValidatorDstAddr: valDstAddr, - SharesAmount: shares, - } - - i++ + msg := stake.NewMsgDelegate(req.DelegatorAddr, req.ValidatorAddr, req.Delegation) + err = msg.ValidateBasic() + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return } - for _, msg := range req.BeginUnbondings { - delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - if !bytes.Equal(info.GetPubKey().Address(), delAddr) { - utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address") - return - } + utils.CompleteAndBroadcastTxREST(w, r, cliCtx, baseReq, []sdk.Msg{msg}, cdc) + } +} - valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddr) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } +func postRedelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req msgBeginRedelegateInput - shares, err := sdk.NewDecFromStr(msg.SharesAmount) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } + err := utils.ReadRESTReq(w, r, cdc, &req) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } - messages[i] = stake.MsgBeginUnbonding{ - DelegatorAddr: delAddr, - ValidatorAddr: valAddr, - SharesAmount: shares, - } + cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly) + cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate) - i++ + baseReq := req.BaseReq.Sanitize() + if !baseReq.ValidateBasic(w, cliCtx) { + return } - simulateGas, gas, err := client.ReadGasFlag(baseReq.Gas) + info, err := kb.Get(baseReq.Name) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error()) return } - adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, baseReq.GasAdjustment, client.DefaultGasAdjustment) - if !ok { + if !bytes.Equal(info.GetPubKey().Address(), req.DelegatorAddr) { + utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address") return } - txBldr := authtxb.TxBuilder{ - Codec: cdc, - Gas: gas, - GasAdjustment: adjustment, - SimulateGas: simulateGas, - ChainID: baseReq.ChainID, + msg := stake.NewMsgBeginRedelegate(req.DelegatorAddr, req.ValidatorSrcAddr, req.ValidatorDstAddr, req.SharesAmount) + err = msg.ValidateBasic() + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return } - // sign messages - signedTxs := make([][]byte, len(messages[:])) - for i, msg := range messages { - // increment sequence for each message - txBldr = txBldr.WithAccountNumber(baseReq.AccountNumber) - txBldr = txBldr.WithSequence(baseReq.Sequence) - - baseReq.Sequence++ - - if baseReq.Simulate || txBldr.SimulateGas { - newBldr, err := utils.EnrichCtxWithGas(txBldr, cliCtx, baseReq.Name, []sdk.Msg{msg}) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } + utils.CompleteAndBroadcastTxREST(w, r, cliCtx, baseReq, []sdk.Msg{msg}, cdc) + } +} - if baseReq.Simulate { - utils.WriteSimulationResponse(w, newBldr.Gas) - return - } +func postUnbondingDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req msgBeginUnbondingInput - txBldr = newBldr - } + err := utils.ReadRESTReq(w, r, cdc, &req) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } - if baseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, txBldr, []sdk.Msg{msg}) - return - } + cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly) + cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate) - txBytes, err := txBldr.BuildAndSign(baseReq.Name, baseReq.Password, []sdk.Msg{msg}) - if err != nil { - utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error()) - return - } + baseReq := req.BaseReq.Sanitize() + if !baseReq.ValidateBasic(w, cliCtx) { + return + } - signedTxs[i] = txBytes + info, err := kb.Get(baseReq.Name) + if err != nil { + utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error()) + return } - // send - // XXX the operation might not be atomic if a tx fails - // should we have a sdk.MultiMsg type to make sending atomic? - results := make([]*ctypes.ResultBroadcastTxCommit, len(signedTxs[:])) - for i, txBytes := range signedTxs { - res, err := cliCtx.BroadcastTx(txBytes) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } + if !bytes.Equal(info.GetPubKey().Address(), req.DelegatorAddr) { + utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address") + return + } - results[i] = res + msg := stake.NewMsgBeginUnbonding(req.DelegatorAddr, req.ValidatorAddr, req.SharesAmount) + err = msg.ValidateBasic() + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return } - utils.PostProcessResponse(w, cdc, results, cliCtx.Indent) + utils.CompleteAndBroadcastTxREST(w, r, cliCtx, baseReq, []sdk.Msg{msg}, cdc) } }