Merge PR #2181: Implement a simulate-only CLI flag/field for REST endpoints

This commit is contained in:
Christopher Goes 2018-08-31 22:35:52 +02:00 committed by GitHub
commit e1981d47e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 532 additions and 265 deletions

View File

@ -40,6 +40,7 @@ FEATURES
* Gaia REST API (`gaiacli advanced rest-server`)
* [lcd] Endpoints to query staking pool and params
* [lcd] \#2110 Add support for `simulate=true` requests query argument to endpoints that send txs to run simulations of transactions
* Gaia CLI (`gaiacli`)
* [cli] Cmds to query staking pool and params
@ -48,6 +49,7 @@ FEATURES
provide desired Bech32 prefix encoding
* [cli] \#2047 Setting the --gas flag value to 0 triggers a simulation of the tx before the actual execution. The gas estimate obtained via the simulation will be used as gas limit in the actual execution.
* [cli] \#2047 The --gas-adjustment flag can be used to adjust the estimate obtained via the simulation triggered by --gas=0.
* [cli] \#2110 Add --dry-run flag to perform a simulation of a transaction without broadcasting it. The --gas flag is ignored as gas would be automatically estimated.
* Gaia
* [cli] #2170 added ability to show the node's address via `gaiad tendermint show-address`

View File

@ -37,6 +37,7 @@ type CLIContext struct {
JSON bool
PrintResponse bool
Certifier tmlite.Certifier
DryRun bool
}
// NewCLIContext returns a new initialized CLIContext with parameters from the
@ -63,6 +64,7 @@ func NewCLIContext() CLIContext {
JSON: viper.GetBool(client.FlagJson),
PrintResponse: viper.GetBool(client.FlagPrintResponse),
Certifier: createCertifier(),
DryRun: viper.GetBool(client.FlagDryRun),
}
}
@ -159,3 +161,9 @@ func (ctx CLIContext) WithCertifier(certifier tmlite.Certifier) CLIContext {
ctx.Certifier = certifier
return ctx
}
// WithGasAdjustment returns a copy of the context with an updated GasAdjustment flag.
func (ctx CLIContext) WithGasAdjustment(adjustment float64) CLIContext {
ctx.GasAdjustment = adjustment
return ctx
}

View File

@ -4,8 +4,11 @@ import "github.com/spf13/cobra"
// nolint
const (
// DefaultGasAdjustment is applied to gas estimates to avoid tx
// execution failures due to state changes that might
// occur between the tx simulation and the actual run.
DefaultGasAdjustment = 1.0
DefaultGasLimit = 200000
DefaultGasAdjustment = 1.2
FlagUseLedger = "ledger"
FlagChainID = "chain-id"
@ -23,6 +26,7 @@ const (
FlagAsync = "async"
FlagJson = "json"
FlagPrintResponse = "print-response"
FlagDryRun = "dry-run"
)
// LineBreak can be included in a command list to provide a blank line
@ -54,11 +58,12 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
c.Flags().String(FlagNode, "tcp://localhost:26657", "<host>:<port> to tendermint rpc interface for this chain")
c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device")
c.Flags().Int64(FlagGas, DefaultGasLimit, "gas limit to set per-transaction; set to 0 to calculate required gas automatically")
c.Flags().Float64(FlagGasAdjustment, DefaultGasAdjustment, "adjustment factor to be multiplied against the estimate returned by the tx simulation")
c.Flags().Float64(FlagGasAdjustment, DefaultGasAdjustment, "adjustment factor to be multiplied against the estimate returned by the tx simulation; if the gas limit is set manually this flag is ignored ")
c.Flags().Bool(FlagAsync, false, "broadcast transactions asynchronously")
c.Flags().Bool(FlagJson, false, "return output in json format")
c.Flags().Bool(FlagPrintResponse, true, "return tx response (only works with async = false)")
c.Flags().Bool(FlagTrustNode, true, "Don't verify proofs for query responses")
c.Flags().Bool(FlagDryRun, false, "ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it")
}
return cmds
}

View File

@ -2,6 +2,7 @@ package lcd
import (
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"regexp"
@ -265,11 +266,21 @@ func TestCoinSend(t *testing.T) {
require.Equal(t, int64(1), mycoins.Amount.Int64())
// test failure with too little gas
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 100)
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 100, 0, "")
require.Equal(t, http.StatusInternalServerError, res.StatusCode, body)
// test success with just enough gas
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 3000)
// test failure with wrong adjustment
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 0, 0.1, "")
require.Equal(t, http.StatusInternalServerError, res.StatusCode, body)
// run simulation and test success with estimated gas
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 0, 0, "?simulate=true")
require.Equal(t, http.StatusOK, res.StatusCode, body)
var responseBody struct {
GasEstimate int64 `json:"gas_estimate"`
}
require.Nil(t, json.Unmarshal([]byte(body), &responseBody))
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, responseBody.GasEstimate, 0, "")
require.Equal(t, http.StatusOK, res.StatusCode, body)
}
@ -720,7 +731,7 @@ func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account {
return acc
}
func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.AccAddress, gas int64) (res *http.Response, body string, receiveAddr sdk.AccAddress) {
func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.AccAddress, gas int64, gasAdjustment float64, queryStr string) (res *http.Response, body string, receiveAddr sdk.AccAddress) {
// create receive address
kb := client.MockKeyBase()
@ -744,22 +755,28 @@ func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.Acc
"gas":"%v",
`, gas)
}
gasAdjustmentStr := ""
if gasAdjustment > 0 {
gasStr = fmt.Sprintf(`
"gas_adjustment":"%v",
`, gasAdjustment)
}
jsonStr := []byte(fmt.Sprintf(`{
%v
%v%v
"name":"%s",
"password":"%s",
"account_number":"%d",
"sequence":"%d",
"amount":[%s],
"chain_id":"%s"
}`, gasStr, name, password, accnum, sequence, coinbz, chainID))
}`, gasStr, gasAdjustmentStr, name, password, accnum, sequence, coinbz, chainID))
res, body = Request(t, port, "POST", fmt.Sprintf("/accounts/%s/send", receiveAddr), jsonStr)
res, body = Request(t, port, "POST", fmt.Sprintf("/accounts/%s/send%v", receiveAddr, queryStr), jsonStr)
return
}
func doSend(t *testing.T, port, seed, name, password string, addr sdk.AccAddress) (receiveAddr sdk.AccAddress, resultTx ctypes.ResultBroadcastTxCommit) {
res, body, receiveAddr := doSendWithGas(t, port, seed, name, password, addr, 0)
res, body, receiveAddr := doSendWithGas(t, port, seed, name, password, addr, 0, 0, "")
require.Equal(t, http.StatusOK, res.StatusCode, body)
err := cdc.UnmarshalJSON([]byte(body), &resultTx)

View File

@ -1,12 +1,45 @@
package utils
import (
"fmt"
"net/http"
"strconv"
)
const (
queryArgDryRun = "simulate"
)
// WriteErrorResponse prepares and writes a HTTP error
// given a status code and an error message.
func WriteErrorResponse(w *http.ResponseWriter, status int, msg string) {
(*w).WriteHeader(status)
(*w).Write([]byte(msg))
func WriteErrorResponse(w http.ResponseWriter, status int, msg string) {
w.WriteHeader(status)
w.Write([]byte(msg))
}
// WriteGasEstimateResponse prepares and writes an HTTP
// response for transactions simulations.
func WriteSimulationResponse(w http.ResponseWriter, gas int64) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(fmt.Sprintf(`{"gas_estimate":%v}`, gas)))
}
// HasDryRunArg returns true if the request's URL query contains
// the dry run argument and its value is set to "true".
func HasDryRunArg(r *http.Request) bool {
return r.URL.Query().Get(queryArgDryRun) == "true"
}
// ParseFloat64OrReturnBadRequest converts s to a float64 value. It returns a default
// value if the string is empty. Write
func ParseFloat64OrReturnBadRequest(w http.ResponseWriter, s string, defaultIfEmpty float64) (n float64, ok bool) {
if len(s) == 0 {
return defaultIfEmpty, true
}
n, err := strconv.ParseFloat(s, 64)
if err != nil {
WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return n, false
}
return n, true
}

View File

@ -12,46 +12,26 @@ import (
"github.com/tendermint/tendermint/libs/common"
)
// DefaultGasAdjustment is applied to gas estimates to avoid tx
// execution failures due to state changes that might
// occur between the tx simulation and the actual run.
const DefaultGasAdjustment = 1.2
// SendTx implements a auxiliary handler that facilitates sending a series of
// messages in a signed transaction given a TxContext and a QueryContext. It
// ensures that the account exists, has a proper number and sequence set. In
// addition, it builds and signs a transaction with the supplied messages.
// Finally, it broadcasts the signed transaction to a node.
func SendTx(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs []sdk.Msg) error {
if err := cliCtx.EnsureAccountExists(); err != nil {
return err
}
from, err := cliCtx.GetFromAddress()
txCtx, err := prepareTxContext(txCtx, cliCtx)
if err != nil {
return err
}
// TODO: (ref #1903) Allow for user supplied account number without
// automatically doing a manual lookup.
if txCtx.AccountNumber == 0 {
accNum, err := cliCtx.GetAccountNumber(from)
autogas := cliCtx.DryRun || (cliCtx.Gas == 0)
if autogas {
txCtx, err = EnrichCtxWithGas(txCtx, cliCtx, cliCtx.FromAddressName, msgs)
if err != nil {
return err
}
txCtx = txCtx.WithAccountNumber(accNum)
fmt.Fprintf(os.Stdout, "estimated gas = %v\n", txCtx.Gas)
}
// TODO: (ref #1903) Allow for user supplied account sequence without
// automatically doing a manual lookup.
if txCtx.Sequence == 0 {
accSeq, err := cliCtx.GetAccountSequence(from)
if err != nil {
return err
}
txCtx = txCtx.WithSequence(accSeq)
if cliCtx.DryRun {
return nil
}
passphrase, err := keys.GetPassphrase(cliCtx.FromAddressName)
@ -59,13 +39,6 @@ func SendTx(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs []sdk.Msg)
return err
}
if cliCtx.Gas == 0 {
txCtx, err = EnrichCtxWithGas(txCtx, cliCtx, cliCtx.FromAddressName, passphrase, msgs)
if err != nil {
return err
}
}
// build and sign the transaction
txBytes, err := txCtx.BuildAndSign(cliCtx.FromAddressName, passphrase, msgs)
if err != nil {
@ -75,24 +48,24 @@ func SendTx(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs []sdk.Msg)
return cliCtx.EnsureBroadcastTx(txBytes)
}
// EnrichCtxWithGas calculates the gas estimate that would be consumed by the
// transaction and set the transaction's respective value accordingly.
func EnrichCtxWithGas(txCtx authctx.TxContext, cliCtx context.CLIContext, name, passphrase string, msgs []sdk.Msg) (authctx.TxContext, error) {
txBytes, err := BuildAndSignTxWithZeroGas(txCtx, name, passphrase, msgs)
// SimulateMsgs simulates the transaction and returns the gas estimate and the adjusted value.
func SimulateMsgs(txCtx authctx.TxContext, cliCtx context.CLIContext, name string, msgs []sdk.Msg, gas int64) (estimated, adjusted int64, err error) {
txBytes, err := txCtx.WithGas(gas).BuildWithPubKey(name, msgs)
if err != nil {
return txCtx, err
return
}
estimate, adjusted, err := CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment)
if err != nil {
return txCtx, err
}
fmt.Fprintf(os.Stderr, "gas: [estimated = %v] [adjusted = %v]\n", estimate, adjusted)
return txCtx.WithGas(adjusted), nil
estimated, adjusted, err = CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment)
return
}
// BuildAndSignTxWithZeroGas builds transactions with GasWanted set to 0.
func BuildAndSignTxWithZeroGas(txCtx authctx.TxContext, name, passphrase string, msgs []sdk.Msg) ([]byte, error) {
return txCtx.WithGas(0).BuildAndSign(name, passphrase, msgs)
// EnrichCtxWithGas calculates the gas estimate that would be consumed by the
// transaction and set the transaction's respective value accordingly.
func EnrichCtxWithGas(txCtx authctx.TxContext, cliCtx context.CLIContext, name string, msgs []sdk.Msg) (authctx.TxContext, error) {
_, adjusted, err := SimulateMsgs(txCtx, cliCtx, name, msgs, 0)
if err != nil {
return txCtx, err
}
return txCtx.WithGas(adjusted), nil
}
// CalculateGas simulates the execution of a transaction and returns
@ -109,14 +82,10 @@ func CalculateGas(queryFunc func(string, common.HexBytes) ([]byte, error), cdc *
return
}
adjusted = adjustGasEstimate(estimate, adjustment)
fmt.Fprintf(os.Stderr, "gas: [estimated = %v] [adjusted = %v]\n", estimate, adjusted)
return
}
func adjustGasEstimate(estimate int64, adjustment float64) int64 {
if adjustment == 0 {
return int64(DefaultGasAdjustment * float64(estimate))
}
return int64(adjustment * float64(estimate))
}
@ -127,3 +96,35 @@ func parseQueryResponse(cdc *amino.Codec, rawRes []byte) (int64, error) {
}
return simulationResult.GasUsed, nil
}
func prepareTxContext(txCtx authctx.TxContext, cliCtx context.CLIContext) (authctx.TxContext, error) {
if err := cliCtx.EnsureAccountExists(); err != nil {
return txCtx, err
}
from, err := cliCtx.GetFromAddress()
if err != nil {
return txCtx, err
}
// TODO: (ref #1903) Allow for user supplied account number without
// automatically doing a manual lookup.
if txCtx.AccountNumber == 0 {
accNum, err := cliCtx.GetAccountNumber(from)
if err != nil {
return txCtx, err
}
txCtx = txCtx.WithAccountNumber(accNum)
}
// TODO: (ref #1903) Allow for user supplied account sequence without
// automatically doing a manual lookup.
if txCtx.Sequence == 0 {
accSeq, err := cliCtx.GetAccountSequence(from)
if err != nil {
return txCtx, err
}
txCtx = txCtx.WithSequence(accSeq)
}
return txCtx, nil
}

View File

@ -58,6 +58,13 @@ func TestGaiaCLISend(t *testing.T) {
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64())
// Test --dry-run
success := executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo --dry-run", flags, barAddr), app.DefaultKeyPass)
require.True(t, success)
// Check state didn't change
fooAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %s %v", fooAddr, flags))
require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64())
// test autosequencing
executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%s --from=foo", flags, barAddr), app.DefaultKeyPass)
tests.WaitForNextNBlocksTM(2, port)
@ -148,6 +155,10 @@ func TestGaiaCLICreateValidator(t *testing.T) {
initialPool.BondedTokens = initialPool.BondedTokens.Add(sdk.NewDec(1))
// Test --dry-run
success := executeWrite(t, cvStr+" --dry-run", app.DefaultKeyPass)
require.True(t, success)
executeWrite(t, cvStr, app.DefaultKeyPass)
tests.WaitForNextNBlocksTM(2, port)
@ -164,7 +175,7 @@ func TestGaiaCLICreateValidator(t *testing.T) {
unbondStr += fmt.Sprintf(" --validator=%s", sdk.ValAddress(barAddr))
unbondStr += fmt.Sprintf(" --shares-amount=%v", "1")
success := executeWrite(t, unbondStr, app.DefaultKeyPass)
success = executeWrite(t, unbondStr, app.DefaultKeyPass)
require.True(t, success)
tests.WaitForNextNBlocksTM(2, port)
@ -211,6 +222,10 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
spStr += fmt.Sprintf(" --title=%s", "Test")
spStr += fmt.Sprintf(" --description=%s", "test")
// Test --dry-run
success := executeWrite(t, spStr+" --dry-run", app.DefaultKeyPass)
require.True(t, success)
executeWrite(t, spStr, app.DefaultKeyPass)
tests.WaitForNextNBlocksTM(2, port)

View File

@ -95,6 +95,8 @@ When you query an account balance with zero tokens, you will get this error: `No
### Send Tokens
The following command could be used to send coins from one account to another:
```bash
gaiacli send \
--amount=10faucetToken \
@ -110,7 +112,7 @@ The `--amount` flag accepts the format `--amount=<value|coin_name>`.
::: tip Note
You may want to cap the maximum gas that can be consumed by the transaction via the `--gas` flag.
If set to 0, the gas limit will be automatically estimated.
Gas estimate might be inaccurate as state changes could occur in between the end of the simulation and the actual execution of a transaction, thus an adjustment is applied on top of the original estimate in order to ensure the transaction is broadcasted successfully. The adjustment can be controlled via the `--gas-adjustment` flag, whose default value is 1.2.
Gas estimate might be inaccurate as state changes could occur in between the end of the simulation and the actual execution of a transaction, thus an adjustment is applied on top of the original estimate in order to ensure the transaction is broadcasted successfully. The adjustment can be controlled via the `--gas-adjustment` flag, whose default value is 1.0.
:::
Now, view the updated balances of the origin and destination accounts:
@ -126,6 +128,17 @@ You can also check your balance at a given block by using the `--block` flag:
gaiacli account <account_cosmos> --block=<block_height>
```
You can simulate a transaction without actually broadcasting it by appending the `--dry-run` flag to the command line:
```bash
gaiacli send \
--amount=10faucetToken \
--chain-id=<chain_id> \
--name=<key_name> \
--to=<destination_cosmosaccaddr> \
--dry-run
```
### Staking
#### Set up a Validator

View File

@ -88,17 +88,17 @@ func TestMsgQuiz(t *testing.T) {
require.Equal(t, acc1, res1)
// Set the trend, submit a really cool quiz and check for reward
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg1}, []int64{0}, []int64{0}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{1}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg1}, []int64{0}, []int64{0}, true, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{1}, true, true, priv1)
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(69)}})
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []int64{0}, []int64{2}, false, priv1) // result without reward
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []int64{0}, []int64{2}, false, false, priv1) // result without reward
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(69)}})
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{3}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{3}, true, true, priv1)
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(138)}})
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg2}, []int64{0}, []int64{4}, true, priv1) // reset the trend
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{5}, false, priv1) // the same answer will nolonger do!
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg2}, []int64{0}, []int64{4}, true, true, priv1) // reset the trend
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{5}, false, false, priv1) // the same answer will nolonger do!
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(138)}})
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []int64{0}, []int64{6}, true, priv1) // earlier answer now relevant again
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []int64{0}, []int64{6}, true, true, priv1) // earlier answer now relevant again
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"badvibesonly", sdk.NewInt(69)}, {"icecold", sdk.NewInt(138)}})
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg3}, []int64{0}, []int64{7}, false, priv1) // expect to fail to set the trend to something which is not cool
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg3}, []int64{0}, []int64{7}, false, false, priv1) // expect to fail to set the trend to something which is not cool
}

View File

@ -74,13 +74,13 @@ func TestMsgMine(t *testing.T) {
// Mine and check for reward
mineMsg1 := GenerateMsgMine(addr1, 1, 2)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg1}, []int64{0}, []int64{0}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg1}, []int64{0}, []int64{0}, true, true, priv1)
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", sdk.NewInt(1)}})
// Mine again and check for reward
mineMsg2 := GenerateMsgMine(addr1, 2, 3)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []int64{0}, []int64{1}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []int64{0}, []int64{1}, true, true, priv1)
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", sdk.NewInt(2)}})
// Mine again - should be invalid
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []int64{0}, []int64{1}, false, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []int64{0}, []int64{1}, false, false, priv1)
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", sdk.NewInt(2)}})
}

View File

@ -65,7 +65,7 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
return newCtx, err.Result(), true
}
sigs := stdTx.GetSignatures()
sigs := stdTx.GetSignatures() // When simulating, this would just be a 0-length slice.
signerAddrs := stdTx.GetSigners()
msgs := tx.GetMsgs()
@ -88,10 +88,7 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
// check signature, return account with incremented nonce
signBytes := StdSignBytes(newCtx.ChainID(), accNums[i], sequences[i], fee, msgs, stdTx.GetMemo())
signerAcc, res := processSig(
newCtx, am,
signerAddr, sig, signBytes,
)
signerAcc, res := processSig(newCtx, am, signerAddr, sig, signBytes, simulate)
if !res.IsOK() {
return newCtx, res, true
}
@ -149,24 +146,24 @@ func validateBasic(tx StdTx) (err sdk.Error) {
// if the account doesn't have a pubkey, set it.
func processSig(
ctx sdk.Context, am AccountMapper,
addr sdk.AccAddress, sig StdSignature, signBytes []byte) (
addr sdk.AccAddress, sig StdSignature, signBytes []byte, simulate bool) (
acc Account, res sdk.Result) {
// Get the account.
acc = am.GetAccount(ctx, addr)
if acc == nil {
return nil, sdk.ErrUnknownAddress(addr.String()).Result()
}
// Check account number.
accnum := acc.GetAccountNumber()
seq := acc.GetSequence()
// Check account number.
if accnum != sig.AccountNumber {
return nil, sdk.ErrInvalidSequence(
fmt.Sprintf("Invalid account number. Got %d, expected %d", sig.AccountNumber, accnum)).Result()
}
// Check and increment sequence number.
seq := acc.GetSequence()
// Check sequence number.
if seq != sig.Sequence {
return nil, sdk.ErrInvalidSequence(
fmt.Sprintf("Invalid sequence. Got %d, expected %d", sig.Sequence, seq)).Result()
@ -176,31 +173,48 @@ func processSig(
// Handle w/ #870
panic(err)
}
pubKey, res := processPubKey(acc, sig, simulate)
if !res.IsOK() {
return nil, res
}
err = acc.SetPubKey(pubKey)
if err != nil {
return nil, sdk.ErrInternal("setting PubKey on signer's account").Result()
}
consumeSignatureVerificationGas(ctx.GasMeter(), pubKey)
if !simulate && !pubKey.VerifyBytes(signBytes, sig.Signature) {
return nil, sdk.ErrUnauthorized("signature verification failed").Result()
}
return
}
func processPubKey(acc Account, sig StdSignature, simulate bool) (crypto.PubKey, sdk.Result) {
// If pubkey is not known for account,
// set it from the StdSignature.
pubKey := acc.GetPubKey()
if simulate {
// In simulate mode the transaction comes with no signatures, thus
// if the account's pubkey is nil, both signature verification
// and gasKVStore.Set() shall consume the largest amount, i.e.
// it takes more gas to verifiy secp256k1 keys than ed25519 ones.
if pubKey == nil {
return secp256k1.GenPrivKey().PubKey(), sdk.Result{}
}
return pubKey, sdk.Result{}
}
if pubKey == nil {
pubKey = sig.PubKey
if pubKey == nil {
return nil, sdk.ErrInvalidPubKey("PubKey not found").Result()
}
if !bytes.Equal(pubKey.Address(), addr) {
if !bytes.Equal(pubKey.Address(), acc.GetAddress()) {
return nil, sdk.ErrInvalidPubKey(
fmt.Sprintf("PubKey does not match Signer address %v", addr)).Result()
}
err = acc.SetPubKey(pubKey)
if err != nil {
return nil, sdk.ErrInternal("setting PubKey on signer's account").Result()
fmt.Sprintf("PubKey does not match Signer address %v", acc.GetAddress())).Result()
}
}
// Check sig.
consumeSignatureVerificationGas(ctx.GasMeter(), pubKey)
if !pubKey.VerifyBytes(signBytes, sig.Signature) {
return nil, sdk.ErrUnauthorized("signature verification failed").Result()
}
return
return pubKey, sdk.Result{}
}
func consumeSignatureVerificationGas(meter sdk.GasMeter, pubkey crypto.PubKey) {

View File

@ -4,14 +4,14 @@ import (
"fmt"
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
wire "github.com/cosmos/cosmos-sdk/wire"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/secp256k1"
"github.com/tendermint/tendermint/libs/log"
sdk "github.com/cosmos/cosmos-sdk/types"
wire "github.com/cosmos/cosmos-sdk/wire"
)
func newTestMsg(addrs ...sdk.AccAddress) *sdk.TestMsg {
@ -567,3 +567,63 @@ func TestAnteHandlerSetPubKey(t *testing.T) {
acc2 = mapper.GetAccount(ctx, addr2)
require.Nil(t, acc2.GetPubKey())
}
func TestProcessPubKey(t *testing.T) {
ms, capKey, _ := setupMultiStore()
cdc := wire.NewCodec()
RegisterBaseAccount(cdc)
mapper := NewAccountMapper(cdc, capKey, ProtoBaseAccount)
ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, log.NewNopLogger())
// keys
_, addr1 := privAndAddr()
priv2, _ := privAndAddr()
acc1 := mapper.NewAccountWithAddress(ctx, addr1)
type args struct {
acc Account
sig StdSignature
simulate bool
}
tests := []struct {
name string
args args
wantErr bool
}{
{"no sigs, simulate off", args{acc1, StdSignature{}, false}, true},
{"no sigs, simulate on", args{acc1, StdSignature{}, true}, false},
{"pubkey doesn't match addr, simulate off", args{acc1, StdSignature{PubKey: priv2.PubKey()}, false}, true},
{"pubkey doesn't match addr, simulate on", args{acc1, StdSignature{PubKey: priv2.PubKey()}, true}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := processPubKey(tt.args.acc, tt.args.sig, tt.args.simulate)
require.Equal(t, tt.wantErr, !err.IsOK())
})
}
}
func TestConsumeSignatureVerificationGas(t *testing.T) {
type args struct {
meter sdk.GasMeter
pubkey crypto.PubKey
}
tests := []struct {
name string
args args
gasConsumed int64
wantPanic bool
}{
{"PubKeyEd25519", args{sdk.NewInfiniteGasMeter(), ed25519.GenPrivKey().PubKey()}, ed25519VerifyCost, false},
{"PubKeySecp256k1", args{sdk.NewInfiniteGasMeter(), secp256k1.GenPrivKey().PubKey()}, secp256k1VerifyCost, false},
{"unknown key", args{sdk.NewInfiniteGasMeter(), nil}, 0, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.wantPanic {
require.Panics(t, func() { consumeSignatureVerificationGas(tt.args.meter, tt.args.pubkey) })
} else {
consumeSignatureVerificationGas(tt.args.meter, tt.args.pubkey)
require.Equal(t, tt.args.meter.GasConsumed(), tt.gasConsumed)
}
})
}
}

View File

@ -148,3 +148,32 @@ func (ctx TxContext) BuildAndSign(name, passphrase string, msgs []sdk.Msg) ([]by
return ctx.Sign(name, passphrase, msg)
}
// BuildWithPubKey builds a single message to be signed from a TxContext given a set of
// messages and attach the public key associated to the given name.
// It returns an error if a fee is supplied but cannot be parsed or the key cannot be
// retrieved.
func (ctx TxContext) BuildWithPubKey(name string, msgs []sdk.Msg) ([]byte, error) {
msg, err := ctx.Build(msgs)
if err != nil {
return nil, err
}
keybase, err := keys.GetKeyBase()
if err != nil {
return nil, err
}
info, err := keybase.Get(name)
if err != nil {
return nil, err
}
sigs := []auth.StdSignature{{
AccountNumber: msg.AccountNumber,
Sequence: msg.Sequence,
PubKey: info.GetPubKey(),
}}
return ctx.Codec.MarshalBinary(auth.NewStdTx(msg.Msgs, msg.Fee, sigs, msg.Memo))
}

View File

@ -33,13 +33,13 @@ func QueryAccountRequestHandlerFn(
addr, err := sdk.AccAddressFromBech32(bech32addr)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
res, err := cliCtx.QueryStore(auth.AddressStoreKey(addr), storeName)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("couldn't query account. Error: %s", err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("couldn't query account. Error: %s", err.Error()))
return
}
@ -52,14 +52,14 @@ func QueryAccountRequestHandlerFn(
// decode the value
account, err := decoder(res)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("couldn't parse query result. Result: %s. Error: %s", res, err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("couldn't parse query result. Result: %s. Error: %s", res, err.Error()))
return
}
// print out whole account
output, err := cdc.MarshalJSON(account)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("couldn't marshall query result. Error: %s", err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("couldn't marshall query result. Error: %s", err.Error()))
return
}

View File

@ -21,6 +21,7 @@ type (
}
appTestCase struct {
expSimPass bool
expPass bool
msgs []sdk.Msg
accNums []int64
@ -107,27 +108,29 @@ func TestMsgSendWithAccounts(t *testing.T) {
testCases := []appTestCase{
{
msgs: []sdk.Msg{sendMsg1},
accNums: []int64{0},
accSeqs: []int64{0},
expPass: true,
privKeys: []crypto.PrivKey{priv1},
msgs: []sdk.Msg{sendMsg1},
accNums: []int64{0},
accSeqs: []int64{0},
expSimPass: true,
expPass: true,
privKeys: []crypto.PrivKey{priv1},
expectedBalances: []expectedBalance{
{addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 57)}},
{addr2, sdk.Coins{sdk.NewInt64Coin("foocoin", 10)}},
},
},
{
msgs: []sdk.Msg{sendMsg1, sendMsg2},
accNums: []int64{0},
accSeqs: []int64{0},
expPass: false,
privKeys: []crypto.PrivKey{priv1},
msgs: []sdk.Msg{sendMsg1, sendMsg2},
accNums: []int64{0},
accSeqs: []int64{0},
expSimPass: false,
expPass: false,
privKeys: []crypto.PrivKey{priv1},
},
}
for _, tc := range testCases {
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expPass, tc.privKeys...)
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)
for _, eb := range tc.expectedBalances {
mock.CheckBalance(t, mapp, eb.addr, eb.coins)
@ -144,7 +147,7 @@ func TestMsgSendWithAccounts(t *testing.T) {
require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
// resigning the tx with the bumped sequence should work
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{sendMsg1, sendMsg2}, []int64{0}, []int64{1}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{sendMsg1, sendMsg2}, []int64{0}, []int64{1}, true, true, priv1)
}
func TestMsgSendMultipleOut(t *testing.T) {
@ -163,11 +166,12 @@ func TestMsgSendMultipleOut(t *testing.T) {
testCases := []appTestCase{
{
msgs: []sdk.Msg{sendMsg2},
accNums: []int64{0},
accSeqs: []int64{0},
expPass: true,
privKeys: []crypto.PrivKey{priv1},
msgs: []sdk.Msg{sendMsg2},
accNums: []int64{0},
accSeqs: []int64{0},
expSimPass: true,
expPass: true,
privKeys: []crypto.PrivKey{priv1},
expectedBalances: []expectedBalance{
{addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 32)}},
{addr2, sdk.Coins{sdk.NewInt64Coin("foocoin", 47)}},
@ -177,7 +181,7 @@ func TestMsgSendMultipleOut(t *testing.T) {
}
for _, tc := range testCases {
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expPass, tc.privKeys...)
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)
for _, eb := range tc.expectedBalances {
mock.CheckBalance(t, mapp, eb.addr, eb.coins)
@ -205,11 +209,12 @@ func TestSengMsgMultipleInOut(t *testing.T) {
testCases := []appTestCase{
{
msgs: []sdk.Msg{sendMsg3},
accNums: []int64{0, 2},
accSeqs: []int64{0, 0},
expPass: true,
privKeys: []crypto.PrivKey{priv1, priv4},
msgs: []sdk.Msg{sendMsg3},
accNums: []int64{0, 2},
accSeqs: []int64{0, 0},
expSimPass: true,
expPass: true,
privKeys: []crypto.PrivKey{priv1, priv4},
expectedBalances: []expectedBalance{
{addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 32)}},
{addr4, sdk.Coins{sdk.NewInt64Coin("foocoin", 32)}},
@ -220,7 +225,7 @@ func TestSengMsgMultipleInOut(t *testing.T) {
}
for _, tc := range testCases {
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expPass, tc.privKeys...)
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)
for _, eb := range tc.expectedBalances {
mock.CheckBalance(t, mapp, eb.addr, eb.coins)
@ -240,22 +245,24 @@ func TestMsgSendDependent(t *testing.T) {
testCases := []appTestCase{
{
msgs: []sdk.Msg{sendMsg1},
accNums: []int64{0},
accSeqs: []int64{0},
expPass: true,
privKeys: []crypto.PrivKey{priv1},
msgs: []sdk.Msg{sendMsg1},
accNums: []int64{0},
accSeqs: []int64{0},
expSimPass: true,
expPass: true,
privKeys: []crypto.PrivKey{priv1},
expectedBalances: []expectedBalance{
{addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 32)}},
{addr2, sdk.Coins{sdk.NewInt64Coin("foocoin", 10)}},
},
},
{
msgs: []sdk.Msg{sendMsg4},
accNums: []int64{1},
accSeqs: []int64{0},
expPass: true,
privKeys: []crypto.PrivKey{priv2},
msgs: []sdk.Msg{sendMsg4},
accNums: []int64{1},
accSeqs: []int64{0},
expSimPass: true,
expPass: true,
privKeys: []crypto.PrivKey{priv2},
expectedBalances: []expectedBalance{
{addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 42)}},
},
@ -263,7 +270,7 @@ func TestMsgSendDependent(t *testing.T) {
}
for _, tc := range testCases {
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expPass, tc.privKeys...)
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)
for _, eb := range tc.expectedBalances {
mock.CheckBalance(t, mapp, eb.addr, eb.coins)

View File

@ -4,6 +4,7 @@ import (
"io/ioutil"
"net/http"
cliclient "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/crypto/keys"
@ -31,6 +32,7 @@ type sendBody struct {
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
Gas int64 `json:"gas"`
GasAdjustment string `json:"gas_adjustment"`
}
var msgCdc = wire.NewCodec()
@ -40,6 +42,7 @@ func init() {
}
// SendRequestHandlerFn - http request handler to send coins to a address
// nolint: gocyclo
func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// collect data
@ -48,32 +51,32 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLICo
to, err := sdk.AccAddressFromBech32(bech32addr)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
var m sendBody
body, err := ioutil.ReadAll(r.Body)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
err = msgCdc.UnmarshalJSON(body, &m)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
info, err := kb.Get(m.LocalAccountName)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error())
utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
return
}
// build message
msg := client.BuildMsg(sdk.AccAddress(info.GetPubKey().Address()), to, m.Amount)
if err != nil { // XXX rechecking same error ?
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
@ -85,10 +88,20 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLICo
Sequence: m.Sequence,
}
if m.Gas == 0 {
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg})
adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, m.GasAdjustment, cliclient.DefaultGasAdjustment)
if !ok {
return
}
cliCtx = cliCtx.WithGasAdjustment(adjustment)
if utils.HasDryRunArg(r) || m.Gas == 0 {
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, []sdk.Msg{msg})
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
if utils.HasDryRunArg(r) {
utils.WriteSimulationResponse(w, txCtx.Gas)
return
}
txCtx = newCtx
@ -96,19 +109,19 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLICo
txBytes, err := txCtx.BuildAndSign(m.LocalAccountName, m.Password, []sdk.Msg{msg})
if err != nil {
utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error())
utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
return
}
res, err := cliCtx.BroadcastTx(txBytes)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
output, err := wire.MarshalJSONIndent(cdc, res)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

View File

@ -77,11 +77,11 @@ func postProposalHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.Hand
msg := gov.NewMsgSubmitProposal(req.Title, req.Description, req.ProposalType, req.Proposer, req.InitialDeposit)
err = msg.ValidateBasic()
if err != nil {
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
signAndBuild(w, cliCtx, req.BaseReq, msg, cdc)
signAndBuild(w, r, cliCtx, req.BaseReq, msg, cdc)
}
}
@ -92,7 +92,7 @@ func depositHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFu
if len(strProposalID) == 0 {
err := errors.New("proposalId required but not specified")
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
@ -114,11 +114,11 @@ func depositHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFu
msg := gov.NewMsgDeposit(req.Depositer, proposalID, req.Amount)
err = msg.ValidateBasic()
if err != nil {
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
signAndBuild(w, cliCtx, req.BaseReq, msg, cdc)
signAndBuild(w, r, cliCtx, req.BaseReq, msg, cdc)
}
}
@ -129,7 +129,7 @@ func voteHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFunc
if len(strProposalID) == 0 {
err := errors.New("proposalId required but not specified")
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
@ -151,11 +151,11 @@ func voteHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFunc
msg := gov.NewMsgVote(req.Voter, proposalID, req.Option)
err = msg.ValidateBasic()
if err != nil {
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
signAndBuild(w, cliCtx, req.BaseReq, msg, cdc)
signAndBuild(w, r, cliCtx, req.BaseReq, msg, cdc)
}
}
@ -166,7 +166,7 @@ func queryProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc {
if len(strProposalID) == 0 {
err := errors.New("proposalId required but not specified")
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
@ -183,13 +183,13 @@ func queryProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc {
bz, err := cdc.MarshalJSON(params)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, err := cliCtx.QueryWithData("custom/gov/proposal", bz)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
@ -205,7 +205,7 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc {
if len(strProposalID) == 0 {
err := errors.New("proposalId required but not specified")
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
@ -216,14 +216,14 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc {
if len(bechDepositerAddr) == 0 {
err := errors.New("depositer address required but not specified")
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
depositerAddr, err := sdk.AccAddressFromBech32(bechDepositerAddr)
if err != nil {
err := errors.Errorf("'%s' needs to be bech32 encoded", RestDepositer)
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
@ -236,13 +236,13 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc {
bz, err := cdc.MarshalJSON(params)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, err := cliCtx.QueryWithData("custom/gov/deposit", bz)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
@ -252,11 +252,11 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc {
res, err := cliCtx.QueryWithData("custom/gov/proposal", cdc.MustMarshalBinary(gov.QueryProposalParams{params.ProposalID}))
if err != nil || len(res) == 0 {
err := errors.Errorf("proposalID [%d] does not exist", proposalID)
utils.WriteErrorResponse(&w, http.StatusNotFound, err.Error())
utils.WriteErrorResponse(w, http.StatusNotFound, err.Error())
return
}
err = errors.Errorf("depositer [%s] did not deposit on proposalID [%d]", bechDepositerAddr, proposalID)
utils.WriteErrorResponse(&w, http.StatusNotFound, err.Error())
utils.WriteErrorResponse(w, http.StatusNotFound, err.Error())
return
}
@ -272,7 +272,7 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc {
if len(strProposalID) == 0 {
err := errors.New("proposalId required but not specified")
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
@ -283,14 +283,14 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc {
if len(bechVoterAddr) == 0 {
err := errors.New("voter address required but not specified")
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr)
if err != nil {
err := errors.Errorf("'%s' needs to be bech32 encoded", RestVoter)
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
@ -302,13 +302,13 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc {
}
bz, err := cdc.MarshalJSON(params)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, err := cliCtx.QueryWithData("custom/gov/vote", bz)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
@ -317,17 +317,17 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc {
if vote.Empty() {
bz, err := cdc.MarshalJSON(gov.QueryProposalParams{params.ProposalID})
if err != nil {
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, err := cliCtx.QueryWithData("custom/gov/proposal", bz)
if err != nil || len(res) == 0 {
err := errors.Errorf("proposalID [%d] does not exist", proposalID)
utils.WriteErrorResponse(&w, http.StatusNotFound, err.Error())
utils.WriteErrorResponse(w, http.StatusNotFound, err.Error())
return
}
err = errors.Errorf("voter [%s] did not deposit on proposalID [%d]", bechVoterAddr, proposalID)
utils.WriteErrorResponse(&w, http.StatusNotFound, err.Error())
utils.WriteErrorResponse(w, http.StatusNotFound, err.Error())
return
}
w.Write(res)
@ -343,7 +343,7 @@ func queryVotesOnProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc {
if len(strProposalID) == 0 {
err := errors.New("proposalId required but not specified")
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
@ -359,13 +359,13 @@ func queryVotesOnProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc {
}
bz, err := cdc.MarshalJSON(params)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
res, err := cliCtx.QueryWithData("custom/gov/votes", bz)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
@ -388,7 +388,7 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc {
voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr)
if err != nil {
err := errors.Errorf("'%s' needs to be bech32 encoded", RestVoter)
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
params.Voter = voterAddr
@ -398,7 +398,7 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc {
depositerAddr, err := sdk.AccAddressFromBech32(bechDepositerAddr)
if err != nil {
err := errors.Errorf("'%s' needs to be bech32 encoded", RestDepositer)
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
params.Depositer = depositerAddr
@ -408,7 +408,7 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc {
proposalStatus, err := gov.ProposalStatusFromString(strProposalStatus)
if err != nil {
err := errors.Errorf("'%s' is not a valid Proposal Status", strProposalStatus)
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
params.ProposalStatus = proposalStatus
@ -423,7 +423,7 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc {
bz, err := cdc.MarshalJSON(params)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
@ -431,7 +431,7 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc {
res, err := cliCtx.QueryWithData("custom/gov/proposals", bz)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

View File

@ -6,6 +6,7 @@ import (
"net/http"
"strconv"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/utils"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -20,17 +21,18 @@ type baseReq struct {
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
Gas int64 `json:"gas"`
GasAdjustment string `json:"gas_adjustment"`
}
func buildReq(w http.ResponseWriter, r *http.Request, cdc *wire.Codec, req interface{}) error {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return err
}
err = cdc.UnmarshalJSON(body, req)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return err
}
return nil
@ -38,27 +40,27 @@ func buildReq(w http.ResponseWriter, r *http.Request, cdc *wire.Codec, req inter
func (req baseReq) baseReqValidate(w http.ResponseWriter) bool {
if len(req.Name) == 0 {
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Name required but not specified")
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Name required but not specified")
return false
}
if len(req.Password) == 0 {
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Password required but not specified")
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Password required but not specified")
return false
}
if len(req.ChainID) == 0 {
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "ChainID required but not specified")
utils.WriteErrorResponse(w, http.StatusUnauthorized, "ChainID required but not specified")
return false
}
if req.AccountNumber < 0 {
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Account Number required but not specified")
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Account Number required but not specified")
return false
}
if req.Sequence < 0 {
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Sequence required but not specified")
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Sequence required but not specified")
return false
}
return true
@ -66,7 +68,7 @@ func (req baseReq) baseReqValidate(w http.ResponseWriter) bool {
// TODO: Build this function out into a more generic base-request
// (probably should live in client/lcd).
func signAndBuild(w http.ResponseWriter, cliCtx context.CLIContext, baseReq baseReq, msg sdk.Msg, cdc *wire.Codec) {
func signAndBuild(w http.ResponseWriter, r *http.Request, cliCtx context.CLIContext, baseReq baseReq, msg sdk.Msg, cdc *wire.Codec) {
var err error
txCtx := authctx.TxContext{
Codec: cdc,
@ -76,29 +78,39 @@ func signAndBuild(w http.ResponseWriter, cliCtx context.CLIContext, baseReq base
Gas: baseReq.Gas,
}
if baseReq.Gas == 0 {
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, baseReq.Name, baseReq.Password, []sdk.Msg{msg})
adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, baseReq.GasAdjustment, client.DefaultGasAdjustment)
if !ok {
return
}
cliCtx = cliCtx.WithGasAdjustment(adjustment)
if utils.HasDryRunArg(r) || baseReq.Gas == 0 {
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, baseReq.Name, []sdk.Msg{msg})
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
if utils.HasDryRunArg(r) {
utils.WriteSimulationResponse(w, txCtx.Gas)
return
}
txCtx = newCtx
}
txBytes, err := txCtx.BuildAndSign(baseReq.Name, baseReq.Password, []sdk.Msg{msg})
if err != nil {
utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error())
utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
return
}
res, err := cliCtx.BroadcastTx(txBytes)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
output, err := wire.MarshalJSONIndent(cdc, res)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

View File

@ -70,10 +70,10 @@ func TestIBCMsgs(t *testing.T) {
Sequence: 0,
}
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{transferMsg}, []int64{0}, []int64{0}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{transferMsg}, []int64{0}, []int64{0}, true, true, priv1)
mock.CheckBalance(t, mapp, addr1, emptyCoins)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{transferMsg}, []int64{0}, []int64{1}, false, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{receiveMsg}, []int64{0}, []int64{2}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{transferMsg}, []int64{0}, []int64{1}, false, false, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{receiveMsg}, []int64{0}, []int64{2}, true, true, priv1)
mock.CheckBalance(t, mapp, addr1, coins)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{receiveMsg}, []int64{0}, []int64{2}, false, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{receiveMsg}, []int64{0}, []int64{2}, false, false, priv1)
}

View File

@ -4,6 +4,7 @@ import (
"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/crypto/keys"
@ -29,10 +30,12 @@ type transferBody struct {
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
Gas int64 `json:"gas"`
GasAdjustment string `json:"gas_adjustment"`
}
// TransferRequestHandler - http request handler to transfer coins to a address
// on a different chain via IBC
// nolint: gocyclo
func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
@ -41,26 +44,26 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.C
to, err := sdk.AccAddressFromBech32(bech32addr)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
var m transferBody
body, err := ioutil.ReadAll(r.Body)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
err = cdc.UnmarshalJSON(body, &m)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
info, err := kb.Get(m.LocalAccountName)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error())
utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
return
}
@ -76,10 +79,20 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.C
Gas: m.Gas,
}
if m.Gas == 0 {
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg})
adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, m.GasAdjustment, client.DefaultGasAdjustment)
if !ok {
return
}
cliCtx = cliCtx.WithGasAdjustment(adjustment)
if utils.HasDryRunArg(r) || m.Gas == 0 {
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, []sdk.Msg{msg})
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
if utils.HasDryRunArg(r) {
utils.WriteSimulationResponse(w, txCtx.Gas)
return
}
txCtx = newCtx
@ -87,19 +100,19 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.C
txBytes, err := txCtx.BuildAndSign(m.LocalAccountName, m.Password, []sdk.Msg{msg})
if err != nil {
utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error())
utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
return
}
res, err := cliCtx.BroadcastTx(txBytes)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
output, err := cdc.MarshalJSON(res)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

View File

@ -61,14 +61,14 @@ func TestCheckAndDeliverGenTx(t *testing.T) {
SignCheckDeliver(
t, mApp.BaseApp, []sdk.Msg{msg},
[]int64{accs[0].GetAccountNumber()}, []int64{accs[0].GetSequence()},
true, privKeys[0],
true, true, privKeys[0],
)
// Signing a tx with the wrong privKey should result in an auth error
res := SignCheckDeliver(
t, mApp.BaseApp, []sdk.Msg{msg},
[]int64{accs[1].GetAccountNumber()}, []int64{accs[1].GetSequence() + 1},
false, privKeys[1],
true, false, privKeys[1],
)
require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
@ -76,7 +76,7 @@ func TestCheckAndDeliverGenTx(t *testing.T) {
SignCheckDeliver(
t, mApp.BaseApp, []sdk.Msg{msg},
[]int64{accs[0].GetAccountNumber()}, []int64{accs[0].GetSequence() + 1},
true, privKeys[0],
true, true, privKeys[0],
)
}

View File

@ -71,13 +71,13 @@ func CheckGenTx(
// returned.
func SignCheckDeliver(
t *testing.T, app *baseapp.BaseApp, msgs []sdk.Msg, accNums []int64,
seq []int64, expPass bool, priv ...crypto.PrivKey,
seq []int64, expSimPass, expPass bool, priv ...crypto.PrivKey,
) sdk.Result {
tx := GenTx(msgs, accNums, seq, priv...)
// Must simulate now as CheckTx doesn't run Msgs anymore
res := app.Simulate(tx)
if expPass {
if expSimPass {
require.Equal(t, sdk.ABCICodeOK, res.Code, res.Log)
} else {
require.NotEqual(t, sdk.ABCICodeOK, res.Code, res.Log)

View File

@ -102,7 +102,7 @@ func TestSlashingMsgs(t *testing.T) {
createValidatorMsg := stake.NewMsgCreateValidator(
sdk.ValAddress(addr1), priv1.PubKey(), bondCoin, description,
)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{createValidatorMsg}, []int64{0}, []int64{0}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{createValidatorMsg}, []int64{0}, []int64{0}, true, true, priv1)
mock.CheckBalance(t, mapp, addr1, sdk.Coins{genCoin.Minus(bondCoin)})
mapp.BeginBlock(abci.RequestBeginBlock{})
@ -116,6 +116,6 @@ func TestSlashingMsgs(t *testing.T) {
checkValidatorSigningInfo(t, mapp, keeper, sdk.ValAddress(addr1), false)
// unjail should fail with unknown validator
res := mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{unjailMsg}, []int64{0}, []int64{1}, false, priv1)
res := mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{unjailMsg}, []int64{0}, []int64{1}, false, false, priv1)
require.Equal(t, sdk.ToABCICode(DefaultCodespace, CodeValidatorNotJailed), res.Code)
}

View File

@ -7,6 +7,7 @@ import (
"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/crypto/keys"
@ -33,37 +34,39 @@ type UnjailBody struct {
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
Gas int64 `json:"gas"`
GasAdjustment string `json:"gas_adjustment"`
ValidatorAddr string `json:"validator_addr"`
}
// nolint: gocyclo
func unjailRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var m UnjailBody
body, err := ioutil.ReadAll(r.Body)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
err = json.Unmarshal(body, &m)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusBadRequest, err.Error())
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
info, err := kb.Get(m.LocalAccountName)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error())
utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
return
}
valAddr, err := sdk.ValAddressFromBech32(m.ValidatorAddr)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
return
}
if !bytes.Equal(info.GetPubKey().Address(), valAddr) {
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own validator address")
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own validator address")
return
}
@ -77,10 +80,20 @@ func unjailRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLI
msg := slashing.NewMsgUnjail(valAddr)
if m.Gas == 0 {
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg})
adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, m.GasAdjustment, client.DefaultGasAdjustment)
if !ok {
return
}
cliCtx = cliCtx.WithGasAdjustment(adjustment)
if utils.HasDryRunArg(r) || m.Gas == 0 {
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, []sdk.Msg{msg})
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
if utils.HasDryRunArg(r) {
utils.WriteSimulationResponse(w, txCtx.Gas)
return
}
txCtx = newCtx
@ -88,19 +101,19 @@ func unjailRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLI
txBytes, err := txCtx.BuildAndSign(m.LocalAccountName, m.Password, []sdk.Msg{msg})
if err != nil {
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own validator address")
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own validator address")
return
}
res, err := cliCtx.BroadcastTx(txBytes)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
output, err := json.MarshalIndent(res, "", " ")
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

View File

@ -131,7 +131,7 @@ func TestStakeMsgs(t *testing.T) {
sdk.ValAddress(addr1), priv1.PubKey(), bondCoin, description,
)
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{createValidatorMsg}, []int64{0}, []int64{0}, true, priv1)
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{createValidatorMsg}, []int64{0}, []int64{0}, true, true, priv1)
mock.CheckBalance(t, mApp, addr1, sdk.Coins{genCoin.Minus(bondCoin)})
mApp.BeginBlock(abci.RequestBeginBlock{})
@ -145,7 +145,7 @@ func TestStakeMsgs(t *testing.T) {
addr1, sdk.ValAddress(addr2), priv2.PubKey(), bondCoin, description,
)
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{createValidatorMsgOnBehalfOf}, []int64{0, 1}, []int64{1, 0}, true, priv1, priv2)
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{createValidatorMsgOnBehalfOf}, []int64{0, 1}, []int64{1, 0}, true, true, priv1, priv2)
mock.CheckBalance(t, mApp, addr1, sdk.Coins{genCoin.Minus(bondCoin).Minus(bondCoin)})
mApp.BeginBlock(abci.RequestBeginBlock{})
@ -161,7 +161,7 @@ func TestStakeMsgs(t *testing.T) {
description = NewDescription("bar_moniker", "", "", "")
editValidatorMsg := NewMsgEditValidator(sdk.ValAddress(addr1), description)
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{editValidatorMsg}, []int64{0}, []int64{2}, true, priv1)
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{editValidatorMsg}, []int64{0}, []int64{2}, true, true, priv1)
validator = checkValidator(t, mApp, keeper, sdk.ValAddress(addr1), true)
require.Equal(t, description, validator.Description)
@ -169,13 +169,13 @@ func TestStakeMsgs(t *testing.T) {
mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin})
delegateMsg := NewMsgDelegate(addr2, sdk.ValAddress(addr1), bondCoin)
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{delegateMsg}, []int64{1}, []int64{1}, true, priv2)
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{delegateMsg}, []int64{1}, []int64{1}, true, true, priv2)
mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin.Minus(bondCoin)})
checkDelegation(t, mApp, keeper, addr2, sdk.ValAddress(addr1), true, sdk.NewDec(10))
// begin unbonding
beginUnbondingMsg := NewMsgBeginUnbonding(addr2, sdk.ValAddress(addr1), sdk.NewDec(10))
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []int64{1}, []int64{2}, true, priv2)
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []int64{1}, []int64{2}, true, true, priv2)
// delegation should exist anymore
checkDelegation(t, mApp, keeper, addr2, sdk.ValAddress(addr1), false, sdk.Dec{})

View File

@ -6,6 +6,7 @@ import (
"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/crypto/keys"
@ -60,6 +61,7 @@ type EditDelegationsBody struct {
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
Gas int64 `json:"gas"`
GasAdjustment string `json:"gas_adjustment"`
Delegations []msgDelegationsInput `json:"delegations"`
BeginUnbondings []msgBeginUnbondingInput `json:"begin_unbondings"`
CompleteUnbondings []msgCompleteUnbondingInput `json:"complete_unbondings"`
@ -106,18 +108,18 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex
for _, msg := range m.Delegations {
delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))
return
}
valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddr)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
return
}
if !bytes.Equal(info.GetPubKey().Address(), delAddr) {
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own delegator address")
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address")
return
}
@ -133,29 +135,29 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex
for _, msg := range m.BeginRedelegates {
delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
return
}
if !bytes.Equal(info.GetPubKey().Address(), delAddr) {
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own delegator address")
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, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
return
}
valDstAddr, err := sdk.ValAddressFromBech32(msg.ValidatorDstAddr)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
return
}
shares, err := sdk.NewDecFromStr(msg.SharesAmount)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error()))
return
}
@ -172,24 +174,24 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex
for _, msg := range m.CompleteRedelegates {
delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))
return
}
valSrcAddr, err := sdk.ValAddressFromBech32(msg.ValidatorSrcAddr)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
return
}
valDstAddr, err := sdk.ValAddressFromBech32(msg.ValidatorDstAddr)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
return
}
if !bytes.Equal(info.GetPubKey().Address(), delAddr) {
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own delegator address")
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address")
return
}
@ -205,24 +207,24 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex
for _, msg := range m.BeginUnbondings {
delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))
return
}
if !bytes.Equal(info.GetPubKey().Address(), delAddr) {
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own delegator address")
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, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
return
}
shares, err := sdk.NewDecFromStr(msg.SharesAmount)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode shares amount. Error: %s", err.Error()))
return
}
@ -238,18 +240,18 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex
for _, msg := range m.CompleteUnbondings {
delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))
return
}
valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddr)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
return
}
if !bytes.Equal(info.GetPubKey().Address(), delAddr) {
utils.WriteErrorResponse(&w, http.StatusUnauthorized, "Must use own delegator address")
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address")
return
}
@ -276,10 +278,20 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex
m.Sequence++
if m.Gas == 0 {
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg})
adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, m.GasAdjustment, client.DefaultGasAdjustment)
if !ok {
return
}
cliCtx = cliCtx.WithGasAdjustment(adjustment)
if utils.HasDryRunArg(r) || m.Gas == 0 {
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, []sdk.Msg{msg})
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
if utils.HasDryRunArg(r) {
utils.WriteSimulationResponse(w, txCtx.Gas)
return
}
txCtx = newCtx
@ -287,7 +299,7 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex
txBytes, err := txCtx.BuildAndSign(m.LocalAccountName, m.Password, []sdk.Msg{msg})
if err != nil {
utils.WriteErrorResponse(&w, http.StatusUnauthorized, err.Error())
utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
return
}
@ -301,7 +313,7 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex
for i, txBytes := range signedTxs {
res, err := cliCtx.BroadcastTx(txBytes)
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
@ -310,7 +322,7 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex
output, err := wire.MarshalJSONIndent(cdc, results[:])
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}