Merge branch 'develop' into jack/dry-rest-tests

This commit is contained in:
Jack Zampolin 2018-12-11 08:44:28 -08:00 committed by GitHub
commit 73efc3c5d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 573 additions and 527 deletions

View File

@ -178,7 +178,7 @@ jobs:
name: Test multi-seed Gaia simulation name: Test multi-seed Gaia simulation
command: | command: |
export PATH="$GOBIN:$PATH" export PATH="$GOBIN:$PATH"
make test_sim_gaia_multi_seed scripts/multisim.sh 25 TestFullGaiaSimulation
test_cover: test_cover:
<<: *defaults <<: *defaults

View File

@ -3,7 +3,8 @@
BREAKING CHANGES BREAKING CHANGES
* Gaia REST API (`gaiacli advanced rest-server`) * 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`) * Gaia CLI (`gaiacli`)
@ -34,6 +35,8 @@ IMPROVEMENTS
* Gaia CLI (`gaiacli`) * Gaia CLI (`gaiacli`)
* Gaia * Gaia
* [\#3021](https://github.com/cosmos/cosmos-sdk/pull/3021) Add `--gentx-dir` to `gaiad collect-gentxs` to specify a directory from which collect and load gentxs.
Add `--output-document` to `gaiad init` to allow one to redirect output to file.
* SDK * SDK

View File

@ -47,7 +47,7 @@ type CLIContext struct {
JSON bool JSON bool
PrintResponse bool PrintResponse bool
Verifier tmlite.Verifier Verifier tmlite.Verifier
DryRun bool Simulate bool
GenerateOnly bool GenerateOnly bool
fromAddress types.AccAddress fromAddress types.AccAddress
fromName string fromName string
@ -85,7 +85,7 @@ func NewCLIContext() CLIContext {
JSON: viper.GetBool(client.FlagJson), JSON: viper.GetBool(client.FlagJson),
PrintResponse: viper.GetBool(client.FlagPrintResponse), PrintResponse: viper.GetBool(client.FlagPrintResponse),
Verifier: verifier, Verifier: verifier,
DryRun: viper.GetBool(client.FlagDryRun), Simulate: viper.GetBool(client.FlagDryRun),
GenerateOnly: viper.GetBool(client.FlagGenerateOnly), GenerateOnly: viper.GetBool(client.FlagGenerateOnly),
fromAddress: fromAddress, fromAddress: fromAddress,
fromName: fromName, fromName: fromName,
@ -244,3 +244,15 @@ func (ctx CLIContext) WithVerifier(verifier tmlite.Verifier) CLIContext {
ctx.Verifier = verifier ctx.Verifier = verifier
return ctx 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
}

View File

@ -43,6 +43,7 @@ const (
FlagSSLHosts = "ssl-hosts" FlagSSLHosts = "ssl-hosts"
FlagSSLCertFile = "ssl-certfile" FlagSSLCertFile = "ssl-certfile"
FlagSSLKeyFile = "ssl-keyfile" FlagSSLKeyFile = "ssl-keyfile"
FlagOutputDocument = "output-document" // inspired by wget -O
) )
// LineBreak can be included in a command list to provide a blank line // LineBreak can be included in a command list to provide a blank line

View File

@ -814,4 +814,4 @@ func TestProposalsQuery(t *testing.T) {
require.Len(t, votes, 2) require.Len(t, votes, 2)
require.True(t, addrs[0].String() == votes[0].Voter.String() || addrs[0].String() == votes[1].Voter.String()) require.True(t, addrs[0].String() == votes[0].Voter.String() || addrs[0].String() == votes[1].Voter.String())
require.True(t, addrs[1].String() == votes[0].Voter.String() || addrs[1].String() == votes[1].Voter.String()) require.True(t, addrs[1].String() == votes[0].Voter.String() || addrs[1].String() == votes[1].Voter.String())
} }

View File

@ -625,71 +625,6 @@ paths:
description: Bech32 AccAddress of Delegator description: Bech32 AccAddress of Delegator
required: true required: true
type: string type: string
post:
summary: Submit delegation
parameters:
- in: body
name: delegation
description: The password of the account to remove from the KMS
schema:
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"
tags:
- ICS21
consumes:
- application/json
produces:
- application/json
responses:
200:
description: OK
schema:
$ref: "#/definitions/BroadcastTxCommitResult"
400:
description: Invalid delegator address or delegation body
401:
description: Key password is wrong
500:
description: Internal Server Error
get: get:
summary: Get all delegations from a delegator summary: Get all delegations from a delegator
tags: tags:
@ -702,12 +637,72 @@ paths:
schema: schema:
type: array type: array
items: items:
type: object $ref: "#/definitions/Delegation"
"$ref": "#/definitions/Delegation"
400: 400:
description: Invalid delegator address description: Invalid delegator address
500: 500:
description: Internal Server Error description: Internal Server Error
post:
summary: Submit delegation
parameters:
- 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"
delegation:
$ref: "#/definitions/Coin"
tags:
- ICS21
consumes:
- application/json
produces:
- application/json
responses:
200:
description: OK
schema:
$ref: "#/definitions/BroadcastTxCommitResult"
400:
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: Query the current delegation between a delegator and a validator
tags:
- ICS21
produces:
- application/json
responses:
200:
description: OK
schema:
$ref: "#/definitions/Delegation"
400:
description: Invalid delegator address or validator address
500:
description: Internal Server Error
/stake/delegators/{delegatorAddr}/unbonding_delegations: /stake/delegators/{delegatorAddr}/unbonding_delegations:
parameters: parameters:
- in: path - in: path
@ -727,12 +722,85 @@ paths:
schema: schema:
type: array type: array
items: items:
type: object $ref: "#/definitions/UnbondingDelegation"
"$ref": "#/definitions/UnbondingDelegation"
400: 400:
description: Invalid delegator address description: Invalid delegator address
500: 500:
description: Internal Server Error description: Internal Server Error
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: Query all unbonding delegations between a delegator and a validator
tags:
- ICS21
produces:
- application/json
responses:
200:
description: OK
schema:
type: array
items:
$ref: "#/definitions/UnbondingDelegation"
400:
description: Invalid delegator address or validator address
500:
description: Internal Server Error
/stake/delegators/{delegatorAddr}/redelegations: /stake/delegators/{delegatorAddr}/redelegations:
parameters: parameters:
- in: path - in: path
@ -752,12 +820,58 @@ paths:
schema: schema:
type: array type: array
items: items:
type: object $ref: "#/definitions/Redelegation"
"$ref": "#/definitions/Redelegation"
400: 400:
description: Invalid delegator address description: Invalid delegator address
500: 500:
description: Internal Server Error description: Internal Server Error
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
consumes:
- application/json
produces:
- application/json
responses:
200:
description: OK
schema:
$ref: "#/definitions/BroadcastTxCommitResult"
400:
description: Invalid delegator address or redelegation request body
401:
description: Key password is wrong
500:
description: Internal Server Error
/stake/delegators/{delegatorAddr}/validators: /stake/delegators/{delegatorAddr}/validators:
parameters: parameters:
- in: path - in: path
@ -835,63 +949,6 @@ paths:
description: Invalid delegator address description: Invalid delegator address
500: 500:
description: Internal Server Error 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: Query the current delegation between a delegator and a validator
tags:
- ICS21
produces:
- application/json
responses:
200:
description: OK
schema:
$ref: "#/definitions/Delegation"
400:
description: Invalid delegator address or validator address
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: Query all unbonding delegations between a delegator and a validator
tags:
- ICS21
produces:
- application/json
responses:
200:
description: OK
schema:
type: array
items:
type: object
"$ref": "#/definitions/UnbondingDelegation"
400:
description: Invalid delegator address or validator address
500:
description: Internal Server Error
/stake/validators: /stake/validators:
get: get:
summary: Get all validator candidates summary: Get all validator candidates
@ -1116,7 +1173,7 @@ paths:
type: object type: object
properties: properties:
base_req: base_req:
"$ref": "#/definitions/BaseReq" $ref: "#/definitions/BaseReq"
responses: responses:
200: 200:
description: OK description: OK
@ -1147,7 +1204,7 @@ paths:
type: object type: object
properties: properties:
base_req: base_req:
"$ref": "#/definitions/BaseReq" $ref: "#/definitions/BaseReq"
title: title:
type: string type: string
description: description:
@ -1156,7 +1213,7 @@ paths:
type: string type: string
example: "text" example: "text"
proposer: proposer:
"$ref": "#/definitions/Address" $ref: "#/definitions/Address"
initial_deposit: initial_deposit:
type: array type: array
items: items:
@ -1165,7 +1222,7 @@ paths:
200: 200:
description: OK description: OK
schema: schema:
"$ref": "#/definitions/BroadcastTxCommitResult" $ref: "#/definitions/BroadcastTxCommitResult"
400: 400:
description: Invalid proposal body description: Invalid proposal body
401: 401:
@ -1201,12 +1258,57 @@ paths:
schema: schema:
type: array type: array
items: items:
"$ref": "#/definitions/TextProposal" $ref: "#/definitions/TextProposal"
400: 400:
description: Invalid query parameters description: Invalid query parameters
500: 500:
description: Internal Server Error description: Internal Server Error
/gov/proposals/{proposalId}:
get:
summary: Query a proposal
description: Query a proposal by id
produces:
- application/json
tags:
- ICS22
parameters:
- type: string
name: proposalId
required: true
in: path
responses:
200:
description: OK
schema:
$ref: "#/definitions/TextProposal"
400:
description: Invalid proposal id
500:
description: Internal Server Error
/gov/proposals/{proposalId}/deposits: /gov/proposals/{proposalId}/deposits:
get:
summary: Query deposits
description: Query deposits by proposalId
produces:
- application/json
tags:
- ICS22
parameters:
- type: string
name: proposalId
required: true
in: path
responses:
200:
description: OK
schema:
type: array
items:
$ref: "#/definitions/Deposit"
400:
description: Invalid proposal id
500:
description: Internal Server Error
post: post:
summary: Deposit tokens to a proposal summary: Deposit tokens to a proposal
description: Send transaction to deposit tokens to a proposal description: Send transaction to deposit tokens to a proposal
@ -1230,9 +1332,9 @@ paths:
type: object type: object
properties: properties:
base_req: base_req:
"$ref": "#/definitions/BaseReq" $ref: "#/definitions/BaseReq"
depositor: depositor:
"$ref": "#/definitions/Address" $ref: "#/definitions/Address"
amount: amount:
type: array type: array
items: items:
@ -1241,146 +1343,13 @@ paths:
200: 200:
description: OK description: OK
schema: schema:
"$ref": "#/definitions/BroadcastTxCommitResult" $ref: "#/definitions/BroadcastTxCommitResult"
400: 400:
description: Invalid proposal id or deposit body description: Invalid proposal id or deposit body
401: 401:
description: Key password is wrong description: Key password is wrong
500: 500:
description: Internal Server Error description: Internal Server Error
get:
summary: Query deposits
description: Query deposits by proposalId
produces:
- application/json
tags:
- ICS22
parameters:
- type: string
name: proposalId
required: true
in: path
responses:
200:
description: OK
schema:
type: array
items:
"$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.
produces:
- application/json
tags:
- ICS22
parameters:
- type: string
description: proposal id
name: proposalId
required: true
in: path
responses:
200:
description: OK
schema:
$ref: "#/definitions/TallyResult"
400:
description: Invalid proposal id
500:
description: Internal Server Error
/gov/proposals/{proposalId}/votes:
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/BroadcastTxCommitResult"
400:
description: Invalid proposal id or vote body
401:
description: Key password is wrong
500:
description: Internal Server Error
get:
summary: Query voters
description: Query voters information by proposalId
produces:
- application/json
tags:
- ICS22
parameters:
- type: string
description: proposal id
name: proposalId
required: true
in: path
responses:
200:
description: OK
schema:
type: array
items:
"$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
produces:
- application/json
tags:
- ICS22
parameters:
- type: string
name: proposalId
required: true
in: path
responses:
200:
description: OK
schema:
"$ref": "#/definitions/TextProposal"
400:
description: Invalid proposal id
500:
description: Internal Server Error
/gov/proposals/{proposalId}/deposits/{depositor}: /gov/proposals/{proposalId}/deposits/{depositor}:
get: get:
summary: Query deposit summary: Query deposit
@ -1411,6 +1380,71 @@ paths:
description: Found no deposit description: Found no deposit
500: 500:
description: Internal Server Error description: Internal Server Error
/gov/proposals/{proposalId}/votes:
get:
summary: Query voters
description: Query voters information by proposalId
produces:
- application/json
tags:
- ICS22
parameters:
- type: string
description: proposal id
name: proposalId
required: true
in: path
responses:
200:
description: OK
schema:
type: array
items:
$ref: "#/definitions/Vote"
400:
description: Invalid proposal id
500:
description: Internal Server Error
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/BroadcastTxCommitResult"
400:
description: Invalid proposal id or vote body
401:
description: Key password is wrong
500:
description: Internal Server Error
/gov/proposals/{proposalId}/votes/{voter}: /gov/proposals/{proposalId}/votes/{voter}:
get: get:
summary: Query vote summary: Query vote
@ -1441,6 +1475,29 @@ paths:
description: Found no vote description: Found no vote
500: 500:
description: Internal Server Error 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.
produces:
- application/json
tags:
- ICS22
parameters:
- type: string
description: proposal id
name: proposalId
required: true
in: path
responses:
200:
description: OK
schema:
$ref: "#/definitions/TallyResult"
400:
description: Invalid proposal id
500:
description: Internal Server Error
/gov/parameters/deposit: /gov/parameters/deposit:
get: get:
summary: Query governance deposit parameters summary: Query governance deposit parameters
@ -1538,7 +1595,7 @@ definitions:
tags: tags:
type: array type: array
items: items:
"$ref": "#/definitions/KVPair" $ref: "#/definitions/KVPair"
example: example:
code: 0 code: 0
data: data data: data
@ -1567,7 +1624,7 @@ definitions:
tags: tags:
type: array type: array
items: items:
"$ref": "#/definitions/KVPair" $ref: "#/definitions/KVPair"
example: example:
code: 5 code: 5
data: data data: data
@ -1868,7 +1925,7 @@ definitions:
total_deposit: total_deposit:
type: array type: array
items: items:
"$ref": "#/definitions/Coin" $ref: "#/definitions/Coin"
voting_start_time: voting_start_time:
type: string type: string
Deposit: Deposit:
@ -1877,11 +1934,11 @@ definitions:
amount: amount:
type: array type: array
items: items:
"$ref": "#/definitions/Coin" $ref: "#/definitions/Coin"
proposal_id: proposal_id:
type: integer type: integer
depositor: depositor:
"$ref": "#/definitions/Address" $ref: "#/definitions/Address"
TallyResult: TallyResult:
type: object type: object
properties: properties:

View File

@ -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 // ValidateBasic performs basic validation of a BaseReq. If custom validation
// logic is needed, the implementing request handler should perform those // logic is needed, the implementing request handler should perform those
// checks manually. // checks manually.
func (br BaseReq) ValidateBasic(w http.ResponseWriter) bool { func (br BaseReq) ValidateBasic(w http.ResponseWriter, cliCtx context.CLIContext) bool {
switch { if !cliCtx.GenerateOnly && !cliCtx.Simulate {
case len(br.Name) == 0: 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") WriteErrorResponse(w, http.StatusUnauthorized, "name required but not specified")
return false 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 return true
} }

View File

@ -34,14 +34,14 @@ func CompleteAndBroadcastTxCli(txBldr authtxb.TxBuilder, cliCtx context.CLIConte
return err return err
} }
if txBldr.SimulateGas || cliCtx.DryRun { if txBldr.SimulateGas || cliCtx.Simulate {
txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, name, msgs) txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, name, msgs)
if err != nil { if err != nil {
return err return err
} }
fmt.Fprintf(os.Stderr, "estimated gas = %v\n", txBldr.Gas) fmt.Fprintf(os.Stderr, "estimated gas = %v\n", txBldr.Gas)
} }
if cliCtx.DryRun { if cliCtx.Simulate {
return nil return nil
} }

View File

@ -645,6 +645,37 @@ trust_node = true
cleanupDirs(gaiadHome, gaiacliHome) cleanupDirs(gaiadHome, gaiacliHome)
} }
func TestGaiadCollectGentxs(t *testing.T) {
t.Parallel()
// Initialise temporary directories
gaiadHome, gaiacliHome := getTestingHomeDirs(t.Name())
gentxDir, err := ioutil.TempDir("", "")
gentxDoc := filepath.Join(gentxDir, "gentx.json")
require.NoError(t, err)
tests.ExecuteT(t, fmt.Sprintf("gaiad --home=%s unsafe-reset-all", gaiadHome), "")
os.RemoveAll(filepath.Join(gaiadHome, "config", "gentx"))
executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s foo", gaiacliHome), app.DefaultKeyPass)
executeWrite(t, fmt.Sprintf("gaiacli keys delete --home=%s bar", gaiacliHome), app.DefaultKeyPass)
executeWriteCheckErr(t, fmt.Sprintf("gaiacli keys add --home=%s foo", gaiacliHome), app.DefaultKeyPass)
executeWriteCheckErr(t, fmt.Sprintf("gaiacli keys add --home=%s bar", gaiacliHome), app.DefaultKeyPass)
executeWriteCheckErr(t, fmt.Sprintf("gaiacli config --home=%s output json", gaiacliHome))
fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show foo --home=%s", gaiacliHome))
// Run init
_ = executeInit(t, fmt.Sprintf("gaiad init -o --moniker=foo --home=%s", gaiadHome))
// Add account to genesis.json
executeWriteCheckErr(t, fmt.Sprintf(
"gaiad add-genesis-account %s 150%s,1000fooToken --home=%s", fooAddr, stakeTypes.DefaultBondDenom, gaiadHome))
executeWrite(t, fmt.Sprintf("cat %s%sconfig%sgenesis.json", gaiadHome, string(os.PathSeparator), string(os.PathSeparator)))
// Write gentx file
executeWriteCheckErr(t, fmt.Sprintf(
"gaiad gentx --name=foo --home=%s --home-client=%s --output-document=%s", gaiadHome, gaiacliHome, gentxDoc), app.DefaultKeyPass)
// Collect gentxs from a custom directory
executeWriteCheckErr(t, fmt.Sprintf("gaiad collect-gentxs --home=%s --gentx-dir=%s", gaiadHome, gentxDir), app.DefaultKeyPass)
cleanupDirs(gaiadHome, gaiacliHome, gentxDir)
}
//___________________________________________________________________________________ //___________________________________________________________________________________
// helper methods // helper methods

View File

@ -18,6 +18,10 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
) )
const (
flagGenTxDir = "gentx-dir"
)
type initConfig struct { type initConfig struct {
ChainID string ChainID string
GenTxsDir string GenTxsDir string
@ -35,7 +39,6 @@ func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command {
config := ctx.Config config := ctx.Config
config.SetRoot(viper.GetString(cli.HomeFlag)) config.SetRoot(viper.GetString(cli.HomeFlag))
name := viper.GetString(client.FlagName) name := viper.GetString(client.FlagName)
nodeID, valPubKey, err := InitializeNodeValidatorFiles(config) nodeID, valPubKey, err := InitializeNodeValidatorFiles(config)
if err != nil { if err != nil {
return err return err
@ -46,19 +49,13 @@ func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command {
return err return err
} }
toPrint := printInfo{ genTxsDir := viper.GetString(flagGenTxDir)
Moniker: config.Moniker, if genTxsDir == "" {
ChainID: genDoc.ChainID, genTxsDir = filepath.Join(config.RootDir, "config", "gentx")
NodeID: nodeID,
} }
initCfg := initConfig{ toPrint := newPrintInfo(config.Moniker, genDoc.ChainID, nodeID, genTxsDir, json.RawMessage(""))
ChainID: genDoc.ChainID, initCfg := newInitConfig(genDoc.ChainID, genTxsDir, name, nodeID, valPubKey)
GenTxsDir: filepath.Join(config.RootDir, "config", "gentx"),
Name: name,
NodeID: nodeID,
ValPubKey: valPubKey,
}
appMessage, err := genAppStateFromConfig(cdc, config, initCfg, genDoc) appMessage, err := genAppStateFromConfig(cdc, config, initCfg, genDoc)
if err != nil { if err != nil {
@ -73,6 +70,9 @@ func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command {
} }
cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory")
cmd.Flags().String(flagGenTxDir, "",
"override default \"gentx\" directory from which collect and execute "+
"genesis transactions; default [--home]/config/gentx/")
return cmd return cmd
} }
@ -117,3 +117,27 @@ func genAppStateFromConfig(
err = ExportGenesisFile(genFile, initCfg.ChainID, nil, appState) err = ExportGenesisFile(genFile, initCfg.ChainID, nil, appState)
return return
} }
func newInitConfig(chainID, genTxsDir, name, nodeID string,
valPubKey crypto.PubKey) initConfig {
return initConfig{
ChainID: chainID,
GenTxsDir: genTxsDir,
Name: name,
NodeID: nodeID,
ValPubKey: valPubKey,
}
}
func newPrintInfo(moniker, chainID, nodeID, genTxsDir string,
appMessage json.RawMessage) printInfo {
return printInfo{
Moniker: moniker,
ChainID: chainID,
NodeID: nodeID,
GenTxsDir: genTxsDir,
AppMessage: appMessage,
}
}

View File

@ -137,9 +137,12 @@ following delegation and commission default parameters:
} }
// Fetch output file name // Fetch output file name
outputDocument, err := makeOutputFilepath(config.RootDir, nodeID) outputDocument := viper.GetString(client.FlagOutputDocument)
if err != nil { if outputDocument == "" {
return err outputDocument, err = makeOutputFilepath(config.RootDir, nodeID)
if err != nil {
return err
}
} }
if err := writeSignedGenTx(cdc, outputDocument, signedTx); err != nil { if err := writeSignedGenTx(cdc, outputDocument, signedTx); err != nil {
@ -154,6 +157,8 @@ following delegation and commission default parameters:
cmd.Flags().String(tmcli.HomeFlag, app.DefaultNodeHome, "node's home directory") cmd.Flags().String(tmcli.HomeFlag, app.DefaultNodeHome, "node's home directory")
cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory") cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory")
cmd.Flags().String(client.FlagName, "", "name of private key with which to sign the gentx") cmd.Flags().String(client.FlagName, "", "name of private key with which to sign the gentx")
cmd.Flags().String(client.FlagOutputDocument, "",
"write the genesis transaction JSON document to the given file instead of the default location")
cmd.Flags().AddFlagSet(cli.FsCommissionCreate) cmd.Flags().AddFlagSet(cli.FsCommissionCreate)
cmd.Flags().AddFlagSet(cli.FsAmount) cmd.Flags().AddFlagSet(cli.FsAmount)
cmd.Flags().AddFlagSet(cli.FsPk) cmd.Flags().AddFlagSet(cli.FsPk)

View File

@ -28,6 +28,7 @@ type printInfo struct {
Moniker string `json:"moniker"` Moniker string `json:"moniker"`
ChainID string `json:"chain_id"` ChainID string `json:"chain_id"`
NodeID string `json:"node_id"` NodeID string `json:"node_id"`
GenTxsDir string `json:"gentxs_dir"`
AppMessage json.RawMessage `json:"app_message"` AppMessage json.RawMessage `json:"app_message"`
} }
@ -77,12 +78,7 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command {
return err return err
} }
toPrint := printInfo{ toPrint := newPrintInfo(config.Moniker, chainID, nodeID, "", appState)
ChainID: chainID,
Moniker: config.Moniker,
NodeID: nodeID,
AppMessage: appState,
}
cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config) cfg.WriteConfigFile(filepath.Join(config.RootDir, "config", "config.toml"), config)

View File

@ -279,13 +279,7 @@ func collectGenFiles(
config.SetRoot(nodeDir) config.SetRoot(nodeDir)
nodeID, valPubKey := nodeIDs[i], valPubKeys[i] nodeID, valPubKey := nodeIDs[i], valPubKeys[i]
initCfg := initConfig{ initCfg := newInitConfig(chainID, gentxsDir, moniker, nodeID, valPubKey)
ChainID: chainID,
GenTxsDir: gentxsDir,
Name: moniker,
NodeID: nodeID,
ValPubKey: valPubKey,
}
genDoc, err := loadGenesisDoc(cdc, config.GenesisFile()) genDoc, err := loadGenesisDoc(cdc, config.GenesisFile())
if err != nil { if err != nil {

View File

@ -49,8 +49,11 @@ func SendRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIC
return return
} }
cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly)
cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate)
baseReq := req.BaseReq.Sanitize() baseReq := req.BaseReq.Sanitize()
if !baseReq.ValidateBasic(w) { if !baseReq.ValidateBasic(w, cliCtx) {
return return
} }

View File

@ -78,8 +78,11 @@ func postProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han
return return
} }
cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly)
cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate)
baseReq := req.BaseReq.Sanitize() baseReq := req.BaseReq.Sanitize()
if !baseReq.ValidateBasic(w) { if !baseReq.ValidateBasic(w, cliCtx) {
return return
} }
@ -123,8 +126,11 @@ func depositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerF
return return
} }
cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly)
cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate)
baseReq := req.BaseReq.Sanitize() baseReq := req.BaseReq.Sanitize()
if !baseReq.ValidateBasic(w) { if !baseReq.ValidateBasic(w, cliCtx) {
return return
} }
@ -162,8 +168,11 @@ func voteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc
return return
} }
cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly)
cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate)
baseReq := req.BaseReq.Sanitize() baseReq := req.BaseReq.Sanitize()
if !baseReq.ValidateBasic(w) { if !baseReq.ValidateBasic(w, cliCtx) {
return return
} }

View File

@ -43,8 +43,11 @@ func TransferRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.
return return
} }
cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly)
cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate)
baseReq := req.BaseReq.Sanitize() baseReq := req.BaseReq.Sanitize()
if !baseReq.ValidateBasic(w) { if !baseReq.ValidateBasic(w, cliCtx) {
return return
} }

View File

@ -38,8 +38,11 @@ func unjailRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CL
return return
} }
cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly)
cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate)
baseReq := req.BaseReq.Sanitize() baseReq := req.BaseReq.Sanitize()
if !baseReq.ValidateBasic(w) { if !baseReq.ValidateBasic(w, cliCtx) {
return return
} }

View File

@ -29,8 +29,6 @@ const (
FlagGenesisFormat = "genesis-format" FlagGenesisFormat = "genesis-format"
FlagNodeID = "node-id" FlagNodeID = "node-id"
FlagIP = "ip" FlagIP = "ip"
FlagOutputDocument = "output-document" // inspired by wget -O
) )
// common flagsets to add to various functions // common flagsets to add to various functions

View File

@ -2,81 +2,72 @@ package rest
import ( import (
"bytes" "bytes"
"io/ioutil"
"net/http" "net/http"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/cosmos/cosmos-sdk/crypto/keys"
sdk "github.com/cosmos/cosmos-sdk/types" 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/cosmos/cosmos-sdk/x/stake"
"github.com/gorilla/mux" "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) { func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, kb keys.Keybase) {
r.HandleFunc( r.HandleFunc(
"/stake/delegators/{delegatorAddr}/delegations", "/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") ).Methods("POST")
} }
type ( type (
msgDelegationsInput struct { msgDelegationsInput struct {
DelegatorAddr string `json:"delegator_addr"` // in bech32 BaseReq utils.BaseReq `json:"base_req"`
ValidatorAddr string `json:"validator_addr"` // in bech32 DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32
Delegation sdk.Coin `json:"delegation"` ValidatorAddr sdk.ValAddress `json:"validator_addr"` // in bech32
Delegation sdk.Coin `json:"delegation"`
} }
msgBeginRedelegateInput struct { msgBeginRedelegateInput struct {
DelegatorAddr string `json:"delegator_addr"` // in bech32 BaseReq utils.BaseReq `json:"base_req"`
ValidatorSrcAddr string `json:"validator_src_addr"` // in bech32 DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32
ValidatorDstAddr string `json:"validator_dst_addr"` // in bech32 ValidatorSrcAddr sdk.ValAddress `json:"validator_src_addr"` // in bech32
SharesAmount string `json:"shares"` ValidatorDstAddr sdk.ValAddress `json:"validator_dst_addr"` // in bech32
SharesAmount sdk.Dec `json:"shares"`
} }
msgBeginUnbondingInput struct { msgBeginUnbondingInput struct {
DelegatorAddr string `json:"delegator_addr"` // in bech32 BaseReq utils.BaseReq `json:"base_req"`
ValidatorAddr string `json:"validator_addr"` // in bech32 DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32
SharesAmount string `json:"shares"` ValidatorAddr sdk.ValAddress `json:"validator_addr"` // in bech32
} SharesAmount sdk.Dec `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"`
} }
) )
// TODO: Split this up into several smaller functions, and remove the above nolint func postDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc {
// 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 {
return func(w http.ResponseWriter, r *http.Request) { 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 { if err != nil {
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return return
} }
err = cdc.UnmarshalJSON(body, &req) cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly)
if err != nil { cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate)
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
baseReq := req.BaseReq.Sanitize() baseReq := req.BaseReq.Sanitize()
if !baseReq.ValidateBasic(w) { if !baseReq.ValidateBasic(w, cliCtx) {
return return
} }
@ -86,182 +77,98 @@ func delegationsRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx conte
return return
} }
// build messages if !bytes.Equal(info.GetPubKey().Address(), req.DelegatorAddr) {
messages := make([]sdk.Msg, len(req.Delegations)+ utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address")
len(req.BeginRedelegates)+ return
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++
} }
for _, msg := range req.BeginRedelegates { msg := stake.NewMsgDelegate(req.DelegatorAddr, req.ValidatorAddr, req.Delegation)
delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr) err = msg.ValidateBasic()
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++
}
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
}
valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddr)
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.MsgBeginUnbonding{
DelegatorAddr: delAddr,
ValidatorAddr: valAddr,
SharesAmount: shares,
}
i++
}
simulateGas, gas, err := client.ReadGasFlag(baseReq.Gas)
if err != nil { if err != nil {
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return return
} }
adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, baseReq.GasAdjustment, client.DefaultGasAdjustment) utils.CompleteAndBroadcastTxREST(w, r, cliCtx, baseReq, []sdk.Msg{msg}, cdc)
if !ok { }
}
func postRedelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req msgBeginRedelegateInput
err := utils.ReadRESTReq(w, r, cdc, &req)
if err != nil {
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return return
} }
txBldr := authtxb.TxBuilder{ cliCtx = cliCtx.WithGenerateOnly(req.BaseReq.GenerateOnly)
Codec: cdc, cliCtx = cliCtx.WithSimulation(req.BaseReq.Simulate)
Gas: gas,
GasAdjustment: adjustment, baseReq := req.BaseReq.Sanitize()
SimulateGas: simulateGas, if !baseReq.ValidateBasic(w, cliCtx) {
ChainID: baseReq.ChainID, return
} }
// sign messages info, err := kb.Get(baseReq.Name)
signedTxs := make([][]byte, len(messages[:])) if err != nil {
for i, msg := range messages { utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
// increment sequence for each message return
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
}
if baseReq.Simulate {
utils.WriteSimulationResponse(w, newBldr.Gas)
return
}
txBldr = newBldr
}
if baseReq.GenerateOnly {
utils.WriteGenerateStdTxResponse(w, txBldr, []sdk.Msg{msg})
return
}
txBytes, err := txBldr.BuildAndSign(baseReq.Name, baseReq.Password, []sdk.Msg{msg})
if err != nil {
utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
return
}
signedTxs[i] = txBytes
} }
// send if !bytes.Equal(info.GetPubKey().Address(), req.DelegatorAddr) {
// XXX the operation might not be atomic if a tx fails utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address")
// should we have a sdk.MultiMsg type to make sending atomic? return
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
}
results[i] = res
} }
utils.PostProcessResponse(w, cdc, results, cliCtx.Indent) 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
}
utils.CompleteAndBroadcastTxREST(w, r, cliCtx, baseReq, []sdk.Msg{msg}, cdc)
}
}
func postUnbondingDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req msgBeginUnbondingInput
err := utils.ReadRESTReq(w, r, cdc, &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, cliCtx) {
return
}
info, err := kb.Get(baseReq.Name)
if err != nil {
utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
return
}
if !bytes.Equal(info.GetPubKey().Address(), req.DelegatorAddr) {
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address")
return
}
msg := stake.NewMsgBeginUnbonding(req.DelegatorAddr, req.ValidatorAddr, req.SharesAmount)
err = msg.ValidateBasic()
if err != nil {
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
utils.CompleteAndBroadcastTxREST(w, r, cliCtx, baseReq, []sdk.Msg{msg}, cdc)
} }
} }