Merge branch 'develop' into bianjie/lcd-lite-swagger-ui

This commit is contained in:
HaoyangLiu
2018-09-13 22:47:01 +08:00
committed by GitHub
176 changed files with 4251 additions and 2006 deletions
+4 -11
View File
@@ -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
}
+5 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
}