Merge branch 'develop' into bianjie/lcd-lite-swagger-ui
This commit is contained in:
@@ -3,10 +3,11 @@ package context
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
@@ -26,8 +27,6 @@ type CLIContext struct {
|
||||
Client rpcclient.Client
|
||||
Logger io.Writer
|
||||
Height int64
|
||||
Gas int64
|
||||
GasAdjustment float64
|
||||
NodeURI string
|
||||
FromAddressName string
|
||||
AccountStore string
|
||||
@@ -38,6 +37,7 @@ type CLIContext struct {
|
||||
PrintResponse bool
|
||||
Certifier tmlite.Certifier
|
||||
DryRun bool
|
||||
GenerateOnly bool
|
||||
}
|
||||
|
||||
// NewCLIContext returns a new initialized CLIContext with parameters from the
|
||||
@@ -56,8 +56,6 @@ func NewCLIContext() CLIContext {
|
||||
AccountStore: ctxAccStoreName,
|
||||
FromAddressName: viper.GetString(client.FlagFrom),
|
||||
Height: viper.GetInt64(client.FlagHeight),
|
||||
Gas: viper.GetInt64(client.FlagGas),
|
||||
GasAdjustment: viper.GetFloat64(client.FlagGasAdjustment),
|
||||
TrustNode: viper.GetBool(client.FlagTrustNode),
|
||||
UseLedger: viper.GetBool(client.FlagUseLedger),
|
||||
Async: viper.GetBool(client.FlagAsync),
|
||||
@@ -65,6 +63,7 @@ func NewCLIContext() CLIContext {
|
||||
PrintResponse: viper.GetBool(client.FlagPrintResponse),
|
||||
Certifier: createCertifier(),
|
||||
DryRun: viper.GetBool(client.FlagDryRun),
|
||||
GenerateOnly: viper.GetBool(client.FlagGenerateOnly),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,9 +160,3 @@ 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
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
@@ -17,7 +19,6 @@ import (
|
||||
tmliteProxy "github.com/tendermint/tendermint/lite/proxy"
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GetNode returns an RPC client. If the context's client is not defined, an
|
||||
@@ -270,11 +271,11 @@ func (ctx CLIContext) ensureBroadcastTx(txBytes []byte) error {
|
||||
type toJSON struct {
|
||||
Height int64
|
||||
TxHash string
|
||||
Response string
|
||||
Response abci.ResponseDeliverTx
|
||||
}
|
||||
|
||||
if ctx.Logger != nil {
|
||||
resJSON := toJSON{res.Height, res.Hash.String(), fmt.Sprintf("%+v", res.DeliverTx)}
|
||||
resJSON := toJSON{res.Height, res.Hash.String(), res.DeliverTx}
|
||||
bz, err := ctx.Codec.MarshalJSON(resJSON)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -339,6 +340,7 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, err erro
|
||||
}
|
||||
|
||||
// verifyProof perform response proof verification
|
||||
// nolint: unparam
|
||||
func (ctx CLIContext) verifyProof(path string, resp abci.ResponseQuery) error {
|
||||
|
||||
if ctx.Certifier == nil {
|
||||
|
||||
+57
-3
@@ -1,6 +1,11 @@
|
||||
package client
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// nolint
|
||||
const (
|
||||
@@ -9,6 +14,7 @@ const (
|
||||
// occur between the tx simulation and the actual run.
|
||||
DefaultGasAdjustment = 1.0
|
||||
DefaultGasLimit = 200000
|
||||
GasFlagSimulate = "simulate"
|
||||
|
||||
FlagUseLedger = "ledger"
|
||||
FlagChainID = "chain-id"
|
||||
@@ -27,11 +33,15 @@ const (
|
||||
FlagJson = "json"
|
||||
FlagPrintResponse = "print-response"
|
||||
FlagDryRun = "dry-run"
|
||||
FlagGenerateOnly = "generate-only"
|
||||
)
|
||||
|
||||
// LineBreak can be included in a command list to provide a blank line
|
||||
// to help with readability
|
||||
var LineBreak = &cobra.Command{Run: func(*cobra.Command, []string) {}}
|
||||
var (
|
||||
LineBreak = &cobra.Command{Run: func(*cobra.Command, []string) {}}
|
||||
GasFlagVar = GasSetting{Gas: DefaultGasLimit}
|
||||
)
|
||||
|
||||
// GetCommands adds common flags to query commands
|
||||
func GetCommands(cmds ...*cobra.Command) []*cobra.Command {
|
||||
@@ -57,13 +67,57 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
|
||||
c.Flags().String(FlagChainID, "", "Chain ID of tendermint node")
|
||||
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; 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")
|
||||
c.Flags().Bool(FlagGenerateOnly, false, "build an unsigned transaction and write it to STDOUT")
|
||||
// --gas can accept integers and "simulate"
|
||||
c.Flags().Var(&GasFlagVar, "gas", fmt.Sprintf(
|
||||
"gas limit to set per-transaction; set to %q to calculate required gas automatically (default %d)", GasFlagSimulate, DefaultGasLimit))
|
||||
}
|
||||
return cmds
|
||||
}
|
||||
|
||||
// Gas flag parsing functions
|
||||
|
||||
// GasSetting encapsulates the possible values passed through the --gas flag.
|
||||
type GasSetting struct {
|
||||
Simulate bool
|
||||
Gas int64
|
||||
}
|
||||
|
||||
// Type returns the flag's value type.
|
||||
func (v *GasSetting) Type() string { return "string" }
|
||||
|
||||
// Set parses and sets the value of the --gas flag.
|
||||
func (v *GasSetting) Set(s string) (err error) {
|
||||
v.Simulate, v.Gas, err = ReadGasFlag(s)
|
||||
return
|
||||
}
|
||||
|
||||
func (v *GasSetting) String() string {
|
||||
if v.Simulate {
|
||||
return GasFlagSimulate
|
||||
}
|
||||
return strconv.FormatInt(v.Gas, 10)
|
||||
}
|
||||
|
||||
// ParseGasFlag parses the value of the --gas flag.
|
||||
func ReadGasFlag(s string) (simulate bool, gas int64, err error) {
|
||||
switch s {
|
||||
case "":
|
||||
gas = DefaultGasLimit
|
||||
case GasFlagSimulate:
|
||||
simulate = true
|
||||
default:
|
||||
gas, err = strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("gas must be either integer or %q", GasFlagSimulate)
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ func BufferStdin() *bufio.Reader {
|
||||
// It enforces the password length
|
||||
func GetPassword(prompt string, buf *bufio.Reader) (pass string, err error) {
|
||||
if inputIsTty() {
|
||||
pass, err = speakeasy.Ask(prompt)
|
||||
pass, err = speakeasy.FAsk(os.Stderr, prompt)
|
||||
} else {
|
||||
pass, err = readLineFromBuf(buf)
|
||||
}
|
||||
|
||||
+40
-46
@@ -4,9 +4,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys"
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
@@ -18,7 +16,7 @@ const (
|
||||
FlagAddress = "address"
|
||||
// FlagPublicKey represents the user's public key on the command line.
|
||||
FlagPublicKey = "pubkey"
|
||||
// FlagBechPrefix defines a desired Bech32 prefix encoding for a key
|
||||
// FlagBechPrefix defines a desired Bech32 prefix encoding for a key.
|
||||
FlagBechPrefix = "bech"
|
||||
)
|
||||
|
||||
@@ -28,39 +26,7 @@ func showKeysCmd() *cobra.Command {
|
||||
Short: "Show key info for the given name",
|
||||
Long: `Return public details of one local key.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
name := args[0]
|
||||
info, err := getKey(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
showAddress := viper.GetBool(FlagAddress)
|
||||
showPublicKey := viper.GetBool(FlagPublicKey)
|
||||
outputSet := cmd.Flag(cli.OutputFlag).Changed
|
||||
|
||||
if showAddress && showPublicKey {
|
||||
return errors.New("cannot use both --address and --pubkey at once")
|
||||
}
|
||||
if outputSet && (showAddress || showPublicKey) {
|
||||
return errors.New("cannot use --output with --address or --pubkey")
|
||||
}
|
||||
|
||||
bechKeyOut, err := getBechKeyOut(viper.GetString(FlagBechPrefix))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
case showAddress:
|
||||
printKeyAddress(info, bechKeyOut)
|
||||
case showPublicKey:
|
||||
printPubKey(info, bechKeyOut)
|
||||
default:
|
||||
printKeyInfo(info, bechKeyOut)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
RunE: runShowCmd,
|
||||
}
|
||||
|
||||
cmd.Flags().String(FlagBechPrefix, "acc", "The Bech32 prefix encoding for a key (acc|val|cons)")
|
||||
@@ -70,6 +36,43 @@ func showKeysCmd() *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runShowCmd(cmd *cobra.Command, args []string) error {
|
||||
name := args[0]
|
||||
|
||||
info, err := GetKeyInfo(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isShowAddr := viper.GetBool(FlagAddress)
|
||||
isShowPubKey := viper.GetBool(FlagPublicKey)
|
||||
isOutputSet := cmd.Flag(cli.OutputFlag).Changed
|
||||
|
||||
if isShowAddr && isShowPubKey {
|
||||
return errors.New("cannot use both --address and --pubkey at once")
|
||||
}
|
||||
|
||||
if isOutputSet && (isShowAddr || isShowPubKey) {
|
||||
return errors.New("cannot use --output with --address or --pubkey")
|
||||
}
|
||||
|
||||
bechKeyOut, err := getBechKeyOut(viper.GetString(FlagBechPrefix))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
case isShowAddr:
|
||||
printKeyAddress(info, bechKeyOut)
|
||||
case isShowPubKey:
|
||||
printPubKey(info, bechKeyOut)
|
||||
default:
|
||||
printKeyInfo(info, bechKeyOut)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getBechKeyOut(bechPrefix string) (bechKeyOutFn, error) {
|
||||
switch bechPrefix {
|
||||
case "acc":
|
||||
@@ -83,15 +86,6 @@ func getBechKeyOut(bechPrefix string) (bechKeyOutFn, error) {
|
||||
return nil, fmt.Errorf("invalid Bech32 prefix encoding provided: %s", bechPrefix)
|
||||
}
|
||||
|
||||
func getKey(name string) (keys.Info, error) {
|
||||
kb, err := GetKeyBase()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return kb.Get(name)
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
// REST
|
||||
|
||||
@@ -112,7 +106,7 @@ func GetKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
info, err := getKey(name)
|
||||
info, err := GetKeyInfo(name)
|
||||
// TODO: check for the error if key actually does not exist, instead of
|
||||
// assuming this as the reason
|
||||
if err != nil {
|
||||
|
||||
+94
-25
@@ -26,6 +26,7 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
"github.com/cosmos/cosmos-sdk/x/slashing"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
@@ -207,8 +208,8 @@ func TestValidators(t *testing.T) {
|
||||
|
||||
require.NotEqual(t, rpc.ResultValidatorsOutput{}, resultVals)
|
||||
|
||||
require.Contains(t, resultVals.Validators[0].Address.String(), "cosmosval")
|
||||
require.Contains(t, resultVals.Validators[0].PubKey, "cosmosconspub")
|
||||
require.Contains(t, resultVals.Validators[0].Address.String(), "cosmosvaloper")
|
||||
require.Contains(t, resultVals.Validators[0].PubKey, "cosmosvalconspub")
|
||||
|
||||
// --
|
||||
|
||||
@@ -268,21 +269,29 @@ 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, 0, "")
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "100", 0, "")
|
||||
require.Equal(t, http.StatusInternalServerError, res.StatusCode, body)
|
||||
|
||||
// test failure with negative gas
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "-200", 0, "")
|
||||
require.Equal(t, http.StatusInternalServerError, res.StatusCode, body)
|
||||
|
||||
// test failure with 0 gas
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "0", 0, "")
|
||||
require.Equal(t, http.StatusInternalServerError, res.StatusCode, body)
|
||||
|
||||
// test failure with wrong adjustment
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, 0, 0.1, "")
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "simulate", 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")
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, "", 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, "")
|
||||
res, body, _ = doSendWithGas(t, port, seed, name, password, addr, fmt.Sprintf("%v", responseBody.GasEstimate), 0, "")
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
}
|
||||
|
||||
@@ -315,6 +324,65 @@ func TestIBCTransfer(t *testing.T) {
|
||||
// TODO: query ibc egress packet state
|
||||
}
|
||||
|
||||
func TestCoinSendGenerateSignAndBroadcast(t *testing.T) {
|
||||
name, password := "test", "1234567890"
|
||||
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
|
||||
cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr})
|
||||
defer cleanup()
|
||||
acc := getAccount(t, port, addr)
|
||||
|
||||
// generate TX
|
||||
res, body, _ := doSendWithGas(t, port, seed, name, password, addr, "simulate", 0, "?generate_only=true")
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var msg auth.StdTx
|
||||
require.Nil(t, cdc.UnmarshalJSON([]byte(body), &msg))
|
||||
require.Equal(t, len(msg.Msgs), 1)
|
||||
require.Equal(t, msg.Msgs[0].Type(), "bank")
|
||||
require.Equal(t, msg.Msgs[0].GetSigners(), []sdk.AccAddress{addr})
|
||||
require.Equal(t, 0, len(msg.Signatures))
|
||||
gasEstimate := msg.Fee.Gas
|
||||
|
||||
// sign tx
|
||||
var signedMsg auth.StdTx
|
||||
accnum := acc.GetAccountNumber()
|
||||
sequence := acc.GetSequence()
|
||||
|
||||
payload := authrest.SignBody{
|
||||
Tx: msg,
|
||||
LocalAccountName: name,
|
||||
Password: password,
|
||||
ChainID: viper.GetString(client.FlagChainID),
|
||||
AccountNumber: accnum,
|
||||
Sequence: sequence,
|
||||
}
|
||||
json, err := cdc.MarshalJSON(payload)
|
||||
require.Nil(t, err)
|
||||
res, body = Request(t, port, "POST", "/tx/sign", json)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
require.Nil(t, cdc.UnmarshalJSON([]byte(body), &signedMsg))
|
||||
require.Equal(t, len(msg.Msgs), len(signedMsg.Msgs))
|
||||
require.Equal(t, msg.Msgs[0].Type(), signedMsg.Msgs[0].Type())
|
||||
require.Equal(t, msg.Msgs[0].GetSigners(), signedMsg.Msgs[0].GetSigners())
|
||||
require.Equal(t, 1, len(signedMsg.Signatures))
|
||||
|
||||
// broadcast tx
|
||||
broadcastPayload := struct {
|
||||
Tx auth.StdTx `json:"tx"`
|
||||
}{Tx: signedMsg}
|
||||
json, err = cdc.MarshalJSON(broadcastPayload)
|
||||
require.Nil(t, err)
|
||||
res, body = Request(t, port, "POST", "/tx/broadcast", json)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
// check if tx was committed
|
||||
var resultTx ctypes.ResultBroadcastTxCommit
|
||||
require.Nil(t, cdc.UnmarshalJSON([]byte(body), &resultTx))
|
||||
require.Equal(t, uint32(0), resultTx.CheckTx.Code)
|
||||
require.Equal(t, uint32(0), resultTx.DeliverTx.Code)
|
||||
require.Equal(t, gasEstimate, resultTx.DeliverTx.GasWanted)
|
||||
require.Equal(t, gasEstimate, resultTx.DeliverTx.GasUsed)
|
||||
}
|
||||
|
||||
func TestTxs(t *testing.T) {
|
||||
name, password := "test", "1234567890"
|
||||
addr, seed := CreateAddr(t, "test", password, GetKeyBase(t))
|
||||
@@ -420,11 +488,12 @@ func TestValidatorsQuery(t *testing.T) {
|
||||
|
||||
// make sure all the validators were found (order unknown because sorted by operator addr)
|
||||
foundVal := false
|
||||
pkBech := sdk.MustBech32ifyConsPub(pks[0])
|
||||
if validators[0].PubKey == pkBech {
|
||||
|
||||
if validators[0].ConsPubKey == pks[0] {
|
||||
foundVal = true
|
||||
}
|
||||
require.True(t, foundVal, "pkBech %v, operator %v", pkBech, validators[0].Operator)
|
||||
|
||||
require.True(t, foundVal, "pk %v, operator %v", pks[0], validators[0].OperatorAddr)
|
||||
}
|
||||
|
||||
func TestValidatorQuery(t *testing.T) {
|
||||
@@ -434,7 +503,7 @@ func TestValidatorQuery(t *testing.T) {
|
||||
|
||||
validator1Operator := sdk.ValAddress(pks[0].Address())
|
||||
validator := getValidator(t, port, validator1Operator)
|
||||
assert.Equal(t, validator.Operator, validator1Operator, "The returned validator does not hold the correct data")
|
||||
assert.Equal(t, validator.OperatorAddr, validator1Operator, "The returned validator does not hold the correct data")
|
||||
}
|
||||
|
||||
func TestBonding(t *testing.T) {
|
||||
@@ -470,11 +539,11 @@ func TestBonding(t *testing.T) {
|
||||
|
||||
bondedValidators := getDelegatorValidators(t, port, addr)
|
||||
require.Len(t, bondedValidators, 1)
|
||||
require.Equal(t, validator1Operator, bondedValidators[0].Operator)
|
||||
require.Equal(t, validator1Operator, bondedValidators[0].OperatorAddr)
|
||||
require.Equal(t, validator.DelegatorShares.Add(sdk.NewDec(60)).String(), bondedValidators[0].DelegatorShares.String())
|
||||
|
||||
bondedValidator := getDelegatorValidator(t, port, addr, validator1Operator)
|
||||
require.Equal(t, validator1Operator, bondedValidator.Operator)
|
||||
require.Equal(t, validator1Operator, bondedValidator.OperatorAddr)
|
||||
|
||||
//////////////////////
|
||||
// testing unbonding
|
||||
@@ -733,7 +802,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, gasAdjustment float64, queryStr string) (res *http.Response, body string, receiveAddr sdk.AccAddress) {
|
||||
func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.AccAddress, gas string, gasAdjustment float64, queryStr string) (res *http.Response, body string, receiveAddr sdk.AccAddress) {
|
||||
|
||||
// create receive address
|
||||
kb := client.MockKeyBase()
|
||||
@@ -752,14 +821,14 @@ func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.Acc
|
||||
}
|
||||
|
||||
gasStr := ""
|
||||
if gas > 0 {
|
||||
if len(gas) != 0 {
|
||||
gasStr = fmt.Sprintf(`
|
||||
"gas":"%v",
|
||||
"gas":%q,
|
||||
`, gas)
|
||||
}
|
||||
gasAdjustmentStr := ""
|
||||
if gasAdjustment > 0 {
|
||||
gasStr = fmt.Sprintf(`
|
||||
gasAdjustmentStr = fmt.Sprintf(`
|
||||
"gas_adjustment":"%v",
|
||||
`, gasAdjustment)
|
||||
}
|
||||
@@ -778,7 +847,7 @@ func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.Acc
|
||||
}
|
||||
|
||||
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, 0, "")
|
||||
res, body, receiveAddr := doSendWithGas(t, port, seed, name, password, addr, "", 0, "")
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
err := cdc.UnmarshalJSON([]byte(body), &resultTx)
|
||||
@@ -900,11 +969,11 @@ func getBondingTxs(t *testing.T, port string, delegatorAddr sdk.AccAddress, quer
|
||||
return txs
|
||||
}
|
||||
|
||||
func getDelegatorValidators(t *testing.T, port string, delegatorAddr sdk.AccAddress) []stake.BechValidator {
|
||||
func getDelegatorValidators(t *testing.T, port string, delegatorAddr sdk.AccAddress) []stake.Validator {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/validators", delegatorAddr), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var bondedValidators []stake.BechValidator
|
||||
var bondedValidators []stake.Validator
|
||||
|
||||
err := cdc.UnmarshalJSON([]byte(body), &bondedValidators)
|
||||
require.Nil(t, err)
|
||||
@@ -912,11 +981,11 @@ func getDelegatorValidators(t *testing.T, port string, delegatorAddr sdk.AccAddr
|
||||
return bondedValidators
|
||||
}
|
||||
|
||||
func getDelegatorValidator(t *testing.T, port string, delAddr sdk.AccAddress, valAddr sdk.ValAddress) stake.BechValidator {
|
||||
func getDelegatorValidator(t *testing.T, port string, delAddr sdk.AccAddress, valAddr sdk.ValAddress) stake.Validator {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/stake/delegators/%s/validators/%s", delAddr, valAddr), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var bondedValidator stake.BechValidator
|
||||
var bondedValidator stake.Validator
|
||||
err := cdc.UnmarshalJSON([]byte(body), &bondedValidator)
|
||||
require.Nil(t, err)
|
||||
|
||||
@@ -1036,19 +1105,19 @@ func doBeginRedelegation(t *testing.T, port, seed, name, password string,
|
||||
return results[0]
|
||||
}
|
||||
|
||||
func getValidators(t *testing.T, port string) []stake.BechValidator {
|
||||
func getValidators(t *testing.T, port string) []stake.Validator {
|
||||
res, body := Request(t, port, "GET", "/stake/validators", nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var validators []stake.BechValidator
|
||||
var validators []stake.Validator
|
||||
err := cdc.UnmarshalJSON([]byte(body), &validators)
|
||||
require.Nil(t, err)
|
||||
return validators
|
||||
}
|
||||
|
||||
func getValidator(t *testing.T, port string, valAddr sdk.ValAddress) stake.BechValidator {
|
||||
func getValidator(t *testing.T, port string, valAddr sdk.ValAddress) stake.Validator {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/stake/validators/%s", valAddr.String()), nil)
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
var validator stake.BechValidator
|
||||
var validator stake.Validator
|
||||
err := cdc.UnmarshalJSON([]byte(body), &validator)
|
||||
require.Nil(t, err)
|
||||
return validator
|
||||
|
||||
@@ -151,7 +151,7 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress
|
||||
|
||||
var validatorsPKs []crypto.PubKey
|
||||
|
||||
// NOTE: It's bad practice to reuse public key address for the owner
|
||||
// NOTE: It's bad practice to reuse public key address for the operator
|
||||
// address but doing in the test for simplicity.
|
||||
var appGenTxs []json.RawMessage
|
||||
for _, gdValidator := range genDoc.Validators {
|
||||
|
||||
+29
-4
@@ -3,11 +3,17 @@ package utils
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
auth "github.com/cosmos/cosmos-sdk/x/auth"
|
||||
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
|
||||
)
|
||||
|
||||
const (
|
||||
queryArgDryRun = "simulate"
|
||||
queryArgDryRun = "simulate"
|
||||
queryArgGenerateOnly = "generate_only"
|
||||
)
|
||||
|
||||
// WriteErrorResponse prepares and writes a HTTP error
|
||||
@@ -26,9 +32,10 @@ func WriteSimulationResponse(w http.ResponseWriter, gas int64) {
|
||||
|
||||
// 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"
|
||||
}
|
||||
func HasDryRunArg(r *http.Request) bool { return urlQueryHasArg(r.URL, queryArgDryRun) }
|
||||
|
||||
// HasGenerateOnlyArg returns whether a URL's query "generate-only" parameter is set to "true".
|
||||
func HasGenerateOnlyArg(r *http.Request) bool { return urlQueryHasArg(r.URL, queryArgGenerateOnly) }
|
||||
|
||||
// ParseFloat64OrReturnBadRequest converts s to a float64 value. It returns a default
|
||||
// value if the string is empty. Write
|
||||
@@ -43,3 +50,21 @@ func ParseFloat64OrReturnBadRequest(w http.ResponseWriter, s string, defaultIfEm
|
||||
}
|
||||
return n, true
|
||||
}
|
||||
|
||||
// WriteGenerateStdTxResponse writes response for the generate_only mode.
|
||||
func WriteGenerateStdTxResponse(w http.ResponseWriter, txBldr authtxb.TxBuilder, msgs []sdk.Msg) {
|
||||
stdMsg, err := txBldr.Build(msgs)
|
||||
if err != nil {
|
||||
WriteErrorResponse(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
output, err := txBldr.Codec.MarshalJSON(auth.NewStdTx(stdMsg.Msgs, stdMsg.Fee, nil, stdMsg.Memo))
|
||||
if err != nil {
|
||||
WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
w.Write(output)
|
||||
return
|
||||
}
|
||||
|
||||
func urlQueryHasArg(url *url.URL, arg string) bool { return url.Query().Get(arg) == "true" }
|
||||
|
||||
+122
-34
@@ -1,34 +1,36 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/context"
|
||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authctx "github.com/cosmos/cosmos-sdk/x/auth/client/context"
|
||||
auth "github.com/cosmos/cosmos-sdk/x/auth"
|
||||
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
|
||||
amino "github.com/tendermint/go-amino"
|
||||
"github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
|
||||
// SendTx implements a auxiliary handler that facilitates sending a series of
|
||||
// messages in a signed transaction given a TxContext and a QueryContext. It
|
||||
// messages in a signed transaction given a TxBuilder 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 {
|
||||
txCtx, err := prepareTxContext(txCtx, cliCtx)
|
||||
func SendTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) error {
|
||||
txBldr, err := prepareTxContext(txBldr, cliCtx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
autogas := cliCtx.DryRun || (cliCtx.Gas == 0)
|
||||
if autogas {
|
||||
txCtx, err = EnrichCtxWithGas(txCtx, cliCtx, cliCtx.FromAddressName, msgs)
|
||||
|
||||
if txBldr.SimulateGas || cliCtx.DryRun {
|
||||
txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, cliCtx.FromAddressName, msgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(os.Stdout, "estimated gas = %v\n", txCtx.Gas)
|
||||
fmt.Fprintf(os.Stderr, "estimated gas = %v\n", txBldr.Gas)
|
||||
}
|
||||
if cliCtx.DryRun {
|
||||
return nil
|
||||
@@ -40,7 +42,7 @@ func SendTx(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs []sdk.Msg)
|
||||
}
|
||||
|
||||
// build and sign the transaction
|
||||
txBytes, err := txCtx.BuildAndSign(cliCtx.FromAddressName, passphrase, msgs)
|
||||
txBytes, err := txBldr.BuildAndSign(cliCtx.FromAddressName, passphrase, msgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -48,31 +50,21 @@ func SendTx(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs []sdk.Msg)
|
||||
return cliCtx.EnsureBroadcastTx(txBytes)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
estimated, adjusted, err = CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment)
|
||||
return
|
||||
}
|
||||
|
||||
// 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)
|
||||
func EnrichCtxWithGas(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, msgs []sdk.Msg) (authtxb.TxBuilder, error) {
|
||||
_, adjusted, err := simulateMsgs(txBldr, cliCtx, name, msgs)
|
||||
if err != nil {
|
||||
return txCtx, err
|
||||
return txBldr, err
|
||||
}
|
||||
return txCtx.WithGas(adjusted), nil
|
||||
return txBldr.WithGas(adjusted), nil
|
||||
}
|
||||
|
||||
// CalculateGas simulates the execution of a transaction and returns
|
||||
// both the estimate obtained by the query and the adjusted amount.
|
||||
func CalculateGas(queryFunc func(string, common.HexBytes) ([]byte, error), cdc *amino.Codec, txBytes []byte, adjustment float64) (estimate, adjusted int64, err error) {
|
||||
// run a simulation (via /app/simulate query) to
|
||||
// estimate gas and update TxContext accordingly
|
||||
// estimate gas and update TxBuilder accordingly
|
||||
rawRes, err := queryFunc("/app/simulate", txBytes)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -85,6 +77,72 @@ func CalculateGas(queryFunc func(string, common.HexBytes) ([]byte, error), cdc *
|
||||
return
|
||||
}
|
||||
|
||||
// PrintUnsignedStdTx builds an unsigned StdTx and prints it to os.Stdout.
|
||||
func PrintUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (err error) {
|
||||
stdTx, err := buildUnsignedStdTx(txBldr, cliCtx, msgs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
json, err := txBldr.Codec.MarshalJSON(stdTx)
|
||||
if err == nil {
|
||||
fmt.Printf("%s\n", json)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SignStdTx appends a signature to a StdTx and returns a copy of a it. If appendSig
|
||||
// is false, it replaces the signatures already attached with the new signature.
|
||||
func SignStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, stdTx auth.StdTx, appendSig bool) (auth.StdTx, error) {
|
||||
var signedStdTx auth.StdTx
|
||||
|
||||
keybase, err := keys.GetKeyBase()
|
||||
if err != nil {
|
||||
return signedStdTx, err
|
||||
}
|
||||
info, err := keybase.Get(name)
|
||||
if err != nil {
|
||||
return signedStdTx, err
|
||||
}
|
||||
addr := info.GetPubKey().Address()
|
||||
|
||||
// Check whether the address is a signer
|
||||
if !isTxSigner(sdk.AccAddress(addr), stdTx.GetSigners()) {
|
||||
fmt.Fprintf(os.Stderr, "WARNING: The generated transaction's intended signer does not match the given signer: '%v'", name)
|
||||
}
|
||||
|
||||
if txBldr.AccountNumber == 0 {
|
||||
accNum, err := cliCtx.GetAccountNumber(addr)
|
||||
if err != nil {
|
||||
return signedStdTx, err
|
||||
}
|
||||
txBldr = txBldr.WithAccountNumber(accNum)
|
||||
}
|
||||
|
||||
if txBldr.Sequence == 0 {
|
||||
accSeq, err := cliCtx.GetAccountSequence(addr)
|
||||
if err != nil {
|
||||
return signedStdTx, err
|
||||
}
|
||||
txBldr = txBldr.WithSequence(accSeq)
|
||||
}
|
||||
|
||||
passphrase, err := keys.GetPassphrase(name)
|
||||
if err != nil {
|
||||
return signedStdTx, err
|
||||
}
|
||||
return txBldr.SignStdTx(name, passphrase, stdTx, appendSig)
|
||||
}
|
||||
|
||||
// SimulateMsgs simulates the transaction and returns the gas estimate and the adjusted value.
|
||||
func simulateMsgs(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, msgs []sdk.Msg) (estimated, adjusted int64, err error) {
|
||||
txBytes, err := txBldr.BuildWithPubKey(name, msgs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
estimated, adjusted, err = CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, txBldr.GasAdjustment)
|
||||
return
|
||||
}
|
||||
|
||||
func adjustGasEstimate(estimate int64, adjustment float64) int64 {
|
||||
return int64(adjustment * float64(estimate))
|
||||
}
|
||||
@@ -97,34 +155,64 @@ func parseQueryResponse(cdc *amino.Codec, rawRes []byte) (int64, error) {
|
||||
return simulationResult.GasUsed, nil
|
||||
}
|
||||
|
||||
func prepareTxContext(txCtx authctx.TxContext, cliCtx context.CLIContext) (authctx.TxContext, error) {
|
||||
func prepareTxContext(txBldr authtxb.TxBuilder, cliCtx context.CLIContext) (authtxb.TxBuilder, error) {
|
||||
if err := cliCtx.EnsureAccountExists(); err != nil {
|
||||
return txCtx, err
|
||||
return txBldr, err
|
||||
}
|
||||
|
||||
from, err := cliCtx.GetFromAddress()
|
||||
if err != nil {
|
||||
return txCtx, err
|
||||
return txBldr, err
|
||||
}
|
||||
|
||||
// TODO: (ref #1903) Allow for user supplied account number without
|
||||
// automatically doing a manual lookup.
|
||||
if txCtx.AccountNumber == 0 {
|
||||
if txBldr.AccountNumber == 0 {
|
||||
accNum, err := cliCtx.GetAccountNumber(from)
|
||||
if err != nil {
|
||||
return txCtx, err
|
||||
return txBldr, err
|
||||
}
|
||||
txCtx = txCtx.WithAccountNumber(accNum)
|
||||
txBldr = txBldr.WithAccountNumber(accNum)
|
||||
}
|
||||
|
||||
// TODO: (ref #1903) Allow for user supplied account sequence without
|
||||
// automatically doing a manual lookup.
|
||||
if txCtx.Sequence == 0 {
|
||||
if txBldr.Sequence == 0 {
|
||||
accSeq, err := cliCtx.GetAccountSequence(from)
|
||||
if err != nil {
|
||||
return txCtx, err
|
||||
return txBldr, err
|
||||
}
|
||||
txCtx = txCtx.WithSequence(accSeq)
|
||||
txBldr = txBldr.WithSequence(accSeq)
|
||||
}
|
||||
return txCtx, nil
|
||||
return txBldr, nil
|
||||
}
|
||||
|
||||
// buildUnsignedStdTx builds a StdTx as per the parameters passed in the
|
||||
// contexts. Gas is automatically estimated if gas wanted is set to 0.
|
||||
func buildUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (stdTx auth.StdTx, err error) {
|
||||
txBldr, err = prepareTxContext(txBldr, cliCtx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if txBldr.SimulateGas {
|
||||
txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, cliCtx.FromAddressName, msgs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "estimated gas = %v\n", txBldr.Gas)
|
||||
}
|
||||
stdSignMsg, err := txBldr.Build(msgs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return auth.NewStdTx(stdSignMsg.Msgs, stdSignMsg.Fee, nil, stdSignMsg.Memo), nil
|
||||
}
|
||||
|
||||
func isTxSigner(user sdk.AccAddress, signers []sdk.AccAddress) bool {
|
||||
for _, s := range signers {
|
||||
if bytes.Equal(user.Bytes(), s.Bytes()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user