Merge PR #3674: Remove password/keybase from REST Client

This commit is contained in:
Alexander Bezobchuk 2019-02-19 13:15:39 -05:00 committed by Jack Zampolin
parent 6967de1073
commit e39debd359
26 changed files with 335 additions and 1335 deletions

View File

@ -6,6 +6,11 @@
### Gaia REST API
* [\#3641] Remove the ability to use a Keybase from the REST API client:
* `password` and `generate_only` have been removed from the `base_req` object
* All txs that used to sign or use the Keybase now only generate the tx
* `keys` routes completely removed
### Gaia CLI
### Gaia

View File

@ -2,10 +2,7 @@ package keys
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"sort"
@ -13,15 +10,13 @@ import (
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/cosmos/cosmos-sdk/crypto/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
"errors"
"github.com/gorilla/mux"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/go-bip39"
bip39 "github.com/cosmos/go-bip39"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/multisig"
@ -304,191 +299,3 @@ func printCreate(info keys.Info, showMnemonic bool, mnemonic string) error {
return nil
}
/////////////////////////////
// REST
// function to just create a new seed to display in the UI before actually persisting it in the keybase
func generateMnemonic(algo keys.SigningAlgo) string {
kb := keys.NewInMemory()
pass := app.DefaultKeyPass
name := "inmemorykey"
_, seed, _ := kb.CreateMnemonic(name, keys.English, pass, algo)
return seed
}
// CheckAndWriteErrorResponse will check for errors and return
// a given error message when corresponding
//TODO: Move to utils/rest or similar
func CheckAndWriteErrorResponse(w http.ResponseWriter, httpErr int, err error) bool {
if err != nil {
w.WriteHeader(httpErr)
_, _ = w.Write([]byte(err.Error()))
return true
}
return false
}
// add new key REST handler
func AddNewKeyRequestHandler(indent bool) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var kb keys.Keybase
var m AddNewKey
kb, err := NewKeyBaseFromHomeFlag()
if CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) {
return
}
body, err := ioutil.ReadAll(r.Body)
if CheckAndWriteErrorResponse(w, http.StatusBadRequest, err) {
return
}
err = json.Unmarshal(body, &m)
if CheckAndWriteErrorResponse(w, http.StatusBadRequest, err) {
return
}
// Check parameters
if m.Name == "" {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errMissingName())
return
}
if m.Password == "" {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errMissingPassword())
return
}
mnemonic := m.Mnemonic
// if mnemonic is empty, generate one
if mnemonic == "" {
mnemonic = generateMnemonic(keys.Secp256k1)
}
if !bip39.IsMnemonicValid(mnemonic) {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidMnemonic())
}
if m.Account < 0 || m.Account > maxValidAccountValue {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidAccountNumber())
return
}
if m.Index < 0 || m.Index > maxValidIndexalue {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidIndexNumber())
return
}
_, err = kb.Get(m.Name)
if err == nil {
CheckAndWriteErrorResponse(w, http.StatusConflict, errKeyNameConflict(m.Name))
return
}
// create account
account := uint32(m.Account)
index := uint32(m.Index)
info, err := kb.CreateAccount(m.Name, mnemonic, keys.DefaultBIP39Passphrase, m.Password, account, index)
if CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) {
return
}
keyOutput, err := Bech32KeyOutput(info)
if CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) {
return
}
keyOutput.Mnemonic = mnemonic
rest.PostProcessResponse(w, cdc, keyOutput, indent)
}
}
// Seed REST request handler
func SeedRequestHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
algoType := vars["type"]
// algo type defaults to secp256k1
if algoType == "" {
algoType = "secp256k1"
}
algo := keys.SigningAlgo(algoType)
seed := generateMnemonic(algo)
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(seed))
}
// RecoverRequestHandler performs key recover request
func RecoverRequestHandler(indent bool) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["name"]
var m RecoverKey
body, err := ioutil.ReadAll(r.Body)
if CheckAndWriteErrorResponse(w, http.StatusBadRequest, err) {
return
}
err = cdc.UnmarshalJSON(body, &m)
if CheckAndWriteErrorResponse(w, http.StatusBadRequest, err) {
return
}
kb, err := NewKeyBaseFromHomeFlag()
CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err)
if name == "" {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errMissingName())
return
}
if m.Password == "" {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errMissingPassword())
return
}
mnemonic := m.Mnemonic
if !bip39.IsMnemonicValid(mnemonic) {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidMnemonic())
}
if m.Mnemonic == "" {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errMissingMnemonic())
return
}
if m.Account < 0 || m.Account > maxValidAccountValue {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidAccountNumber())
return
}
if m.Index < 0 || m.Index > maxValidIndexalue {
CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidIndexNumber())
return
}
_, err = kb.Get(name)
if err == nil {
CheckAndWriteErrorResponse(w, http.StatusConflict, errKeyNameConflict(name))
return
}
account := uint32(m.Account)
index := uint32(m.Index)
info, err := kb.CreateAccount(name, mnemonic, keys.DefaultBIP39Passphrase, m.Password, account, index)
if CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) {
return
}
keyOutput, err := Bech32KeyOutput(info)
if CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) {
return
}
rest.PostProcessResponse(w, cdc, keyOutput, indent)
}
}

View File

@ -2,13 +2,9 @@ package keys
import (
"bufio"
"net/http"
"strings"
"testing"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
"github.com/spf13/viper"
"github.com/tendermint/tendermint/libs/cli"
@ -61,39 +57,3 @@ func Test_runAddCmdBasic(t *testing.T) {
err = runAddCmd(cmd, []string{"keyname2"})
assert.NoError(t, err)
}
type MockResponseWriter struct {
dataHeaderStatus int
dataBody []byte
}
func (MockResponseWriter) Header() http.Header {
panic("Unexpected call!")
}
func (w *MockResponseWriter) Write(data []byte) (int, error) {
w.dataBody = append(w.dataBody, data...)
return len(data), nil
}
func (w *MockResponseWriter) WriteHeader(statusCode int) {
w.dataHeaderStatus = statusCode
}
func TestCheckAndWriteErrorResponse(t *testing.T) {
mockRW := MockResponseWriter{}
mockRW.WriteHeader(100)
assert.Equal(t, 100, mockRW.dataHeaderStatus)
detected := CheckAndWriteErrorResponse(&mockRW, http.StatusBadRequest, errors.New("some ERROR"))
require.True(t, detected)
require.Equal(t, http.StatusBadRequest, mockRW.dataHeaderStatus)
require.Equal(t, "some ERROR", string(mockRW.dataBody[:]))
mockRW = MockResponseWriter{}
detected = CheckAndWriteErrorResponse(&mockRW, http.StatusBadRequest, nil)
require.False(t, detected)
require.Equal(t, 0, mockRW.dataHeaderStatus)
require.Equal(t, "", string(mockRW.dataBody[:]))
}

View File

@ -2,19 +2,14 @@ package keys
import (
"bufio"
"encoding/json"
"errors"
"fmt"
"net/http"
"os"
"github.com/spf13/viper"
"github.com/gorilla/mux"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/crypto/keys/keyerror"
"github.com/spf13/cobra"
)
@ -101,51 +96,3 @@ func confirmDeletion(buf *bufio.Reader) error {
}
return nil
}
////////////////////////
// REST
// delete key request REST body
type DeleteKeyBody struct {
Password string `json:"password"`
}
// delete key REST handler
func DeleteKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["name"]
var kb keys.Keybase
var m DeleteKeyBody
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&m)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(err.Error()))
return
}
kb, err = NewKeyBaseFromHomeFlag()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
err = kb.Delete(name, m.Password, false)
if keyerror.IsErrKeyNotFound(err) {
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte(err.Error()))
return
} else if keyerror.IsErrWrongPassword(err) {
w.WriteHeader(http.StatusUnauthorized)
_, _ = w.Write([]byte(err.Error()))
return
} else if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
w.WriteHeader(http.StatusOK)
}

View File

@ -1,9 +1,6 @@
package keys
import (
"net/http"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/spf13/cobra"
)
@ -29,36 +26,3 @@ func runListCmd(cmd *cobra.Command, args []string) error {
}
return err
}
/////////////////////////
// REST
// query key list REST handler
func QueryKeysRequestHandler(indent bool) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
kb, err := NewKeyBaseFromHomeFlag()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
infos, err := kb.List()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
// an empty list will be JSONized as null, but we want to keep the empty list
if len(infos) == 0 {
rest.PostProcessResponse(w, cdc, []string{}, indent)
return
}
keysOutput, err := Bech32KeysOutput(infos)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
rest.PostProcessResponse(w, cdc, keysOutput, indent)
}
}

View File

@ -1,7 +1,6 @@
package keys
import (
"github.com/gorilla/mux"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client"
@ -30,14 +29,3 @@ func Commands() *cobra.Command {
)
return cmd
}
// resgister REST routes
func RegisterRoutes(r *mux.Router, indent bool) {
r.HandleFunc("/keys", QueryKeysRequestHandler(indent)).Methods("GET")
r.HandleFunc("/keys", AddNewKeyRequestHandler(indent)).Methods("POST")
r.HandleFunc("/keys/seed", SeedRequestHandler).Methods("GET")
r.HandleFunc("/keys/{name}/recover", RecoverRequestHandler(indent)).Methods("POST")
r.HandleFunc("/keys/{name}", GetKeyRequestHandler(indent)).Methods("GET")
r.HandleFunc("/keys/{name}", UpdateKeyRequestHandler).Methods("PUT")
r.HandleFunc("/keys/{name}", DeleteKeyRequestHandler).Methods("DELETE")
}

View File

@ -4,8 +4,6 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/gorilla/mux"
)
func TestCommands(t *testing.T) {
@ -15,8 +13,3 @@ func TestCommands(t *testing.T) {
// Commands are registered
assert.Equal(t, 7, len(rootCommands.Commands()))
}
func TestRegisterRoutes(t *testing.T) {
fakeRouter := mux.Router{}
RegisterRoutes(&fakeRouter, false)
}

View File

@ -2,22 +2,18 @@ package keys
import (
"fmt"
"net/http"
"github.com/tendermint/tendermint/crypto"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/cosmos/cosmos-sdk/types/rest"
"errors"
"github.com/gorilla/mux"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/tendermint/tendermint/crypto/multisig"
"github.com/tendermint/tendermint/libs/cli"
"github.com/cosmos/cosmos-sdk/crypto/keys/keyerror"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -149,46 +145,3 @@ func getBechKeyOut(bechPrefix string) (bechKeyOutFn, error) {
return nil, fmt.Errorf("invalid Bech32 prefix encoding provided: %s", bechPrefix)
}
///////////////////////////
// REST
// get key REST handler
func GetKeyRequestHandler(indent bool) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["name"]
bechPrefix := r.URL.Query().Get(FlagBechPrefix)
if bechPrefix == "" {
bechPrefix = sdk.PrefixAccount
}
bechKeyOut, err := getBechKeyOut(bechPrefix)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(err.Error()))
return
}
info, err := GetKeyInfo(name)
if keyerror.IsErrKeyNotFound(err) {
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte(err.Error()))
return
} else if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
keyOutput, err := bechKeyOut(info)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
rest.PostProcessResponse(w, cdc, keyOutput, indent)
}
}

View File

@ -1,18 +1,11 @@
package keys
import (
"encoding/json"
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/crypto/keys"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/crypto/keys/keyerror"
)
func updateKeyCommand() *cobra.Command {
@ -52,55 +45,3 @@ func runUpdateCmd(cmd *cobra.Command, args []string) error {
fmt.Println("Password successfully updated!")
return nil
}
///////////////////////
// REST
// update key request REST body
type UpdateKeyBody struct {
NewPassword string `json:"new_password"`
OldPassword string `json:"old_password"`
}
// update key REST handler
func UpdateKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
name := vars["name"]
var kb keys.Keybase
var m UpdateKeyBody
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&m)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(err.Error()))
return
}
kb, err = NewKeyBaseFromHomeFlag()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
getNewpass := func() (string, error) { return m.NewPassword, nil }
err = kb.Update(name, m.OldPassword, getNewpass)
if keyerror.IsErrKeyNotFound(err) {
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte(err.Error()))
return
} else if keyerror.IsErrWrongPassword(err) {
w.WriteHeader(http.StatusUnauthorized)
_, _ = w.Write([]byte(err.Error()))
return
} else if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
}

View File

@ -11,7 +11,6 @@ import (
"testing"
"time"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/client"
@ -23,7 +22,6 @@ import (
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/x/auth"
authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest"
"github.com/cosmos/cosmos-sdk/x/bank"
dclcommon "github.com/cosmos/cosmos-sdk/x/distribution/client/common"
distrrest "github.com/cosmos/cosmos-sdk/x/distribution/client/rest"
@ -48,119 +46,6 @@ func init() {
version.Version = os.Getenv("VERSION")
}
func TestSeedsAreDifferent(t *testing.T) {
kb, err := keys.NewKeyBaseFromDir(InitClientHome(t, ""))
require.NoError(t, err)
addr, _ := CreateAddr(t, name1, pw, kb)
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, true)
defer cleanup()
mnemonic1 := getKeysSeed(t, port)
mnemonic2 := getKeysSeed(t, port)
require.NotEqual(t, mnemonic1, mnemonic2)
}
func TestKeyRecover(t *testing.T) {
kb, err := keys.NewKeyBaseFromDir(InitClientHome(t, ""))
require.NoError(t, err)
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}, true)
defer cleanup()
myName1 := "TestKeyRecover_1"
myName2 := "TestKeyRecover_2"
mnemonic := getKeysSeed(t, port)
expectedInfo, _ := kb.CreateAccount(myName1, mnemonic, "", pw, 0, 0)
expectedAddress := expectedInfo.GetAddress().String()
expectedPubKey := sdk.MustBech32ifyAccPub(expectedInfo.GetPubKey())
// recover key
doRecoverKey(t, port, myName2, pw, mnemonic, 0, 0)
keys := getKeys(t, port)
require.Equal(t, expectedAddress, keys[0].Address)
require.Equal(t, expectedPubKey, keys[0].PubKey)
}
func TestKeyRecoverHDPath(t *testing.T) {
kb, err := keys.NewKeyBaseFromDir(InitClientHome(t, ""))
require.NoError(t, err)
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}, true)
defer cleanup()
mnemonic := getKeysSeed(t, port)
for account := uint32(0); account < 50; account += 13 {
for index := uint32(0); index < 50; index += 15 {
name1Idx := fmt.Sprintf("name1_%d_%d", account, index)
name2Idx := fmt.Sprintf("name2_%d_%d", account, index)
expectedInfo, _ := kb.CreateAccount(name1Idx, mnemonic, "", pw, account, index)
expectedAddress := expectedInfo.GetAddress().String()
expectedPubKey := sdk.MustBech32ifyAccPub(expectedInfo.GetPubKey())
// recover key
doRecoverKey(t, port, name2Idx, pw, mnemonic, account, index)
keysName2Idx := getKey(t, port, name2Idx)
require.Equal(t, expectedAddress, keysName2Idx.Address)
require.Equal(t, expectedPubKey, keysName2Idx.PubKey)
}
}
}
func TestKeys(t *testing.T) {
kb, err := keys.NewKeyBaseFromDir(InitClientHome(t, ""))
require.NoError(t, err)
addr1, _ := CreateAddr(t, name1, pw, kb)
addr1Bech32 := addr1.String()
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr1}, true)
defer cleanup()
// get new seed & recover key
mnemonic2 := getKeysSeed(t, port)
doRecoverKey(t, port, name2, pw, mnemonic2, 0, 0)
// add key
mnemonic3 := mnemonic2
resp := doKeysPost(t, port, name3, pw, mnemonic3, 0, 0)
addr3Bech32 := resp.Address
_, err = sdk.AccAddressFromBech32(addr3Bech32)
require.NoError(t, err, "Failed to return a correct bech32 address")
// test if created account is the correct account
expectedInfo3, _ := kb.CreateAccount(name3, mnemonic3, "", pw, 0, 0)
expectedAddress3 := sdk.AccAddress(expectedInfo3.GetPubKey().Address()).String()
require.Equal(t, expectedAddress3, addr3Bech32)
// existing keys
require.Equal(t, name1, getKey(t, port, name1).Name, "Did not serve keys name correctly")
require.Equal(t, addr1Bech32, getKey(t, port, name1).Address, "Did not serve keys Address correctly")
require.Equal(t, name2, getKey(t, port, name2).Name, "Did not serve keys name correctly")
require.Equal(t, addr3Bech32, getKey(t, port, name2).Address, "Did not serve keys Address correctly")
require.Equal(t, name3, getKey(t, port, name3).Name, "Did not serve keys name correctly")
require.Equal(t, addr3Bech32, getKey(t, port, name3).Address, "Did not serve keys Address correctly")
// select key
key := getKey(t, port, name3)
require.Equal(t, name3, key.Name, "Did not serve keys name correctly")
require.Equal(t, addr3Bech32, key.Address, "Did not serve keys Address correctly")
// update key
updateKey(t, port, name3, pw, altPw, false)
// here it should say unauthorized as we changed the password before
updateKey(t, port, name3, pw, altPw, true)
// delete key
deleteKey(t, port, name3, altPw)
}
func TestVersion(t *testing.T) {
// skip the test if the VERSION environment variable has not been set
if version.Version == "" {
@ -255,7 +140,7 @@ func TestCoinSend(t *testing.T) {
require.Equal(t, int64(1), coins2[0].Amount.Int64())
// test failure with too little gas
res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, "100", 0, false, false, fees)
res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, "100", 0, false, true, fees)
require.Equal(t, http.StatusInternalServerError, res.StatusCode, body)
require.Nil(t, err)
@ -268,11 +153,11 @@ func TestCoinSend(t *testing.T) {
require.Equal(t, http.StatusBadRequest, res.StatusCode, body)
// test failure with 0 gas
res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, "0", 0, false, false, fees)
res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, "0", 0, false, true, fees)
require.Equal(t, http.StatusInternalServerError, res.StatusCode, body)
// test failure with wrong adjustment
res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, client.GasFlagAuto, 0.1, false, false, fees)
res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, client.GasFlagAuto, 0.1, false, true, fees)
require.Equal(t, http.StatusInternalServerError, res.StatusCode, body)
@ -291,7 +176,7 @@ func TestCoinSend(t *testing.T) {
// run successful tx
gas := fmt.Sprintf("%d", gasEstResp.GasEstimate)
res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, gas, 1.0, false, false, fees)
res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, gas, 1.0, false, true, fees)
require.Equal(t, http.StatusOK, res.StatusCode, body)
err = cdc.UnmarshalJSON([]byte(body), &resultTx)
@ -316,7 +201,9 @@ func TestCoinSendAccAuto(t *testing.T) {
initialBalance := acc.GetCoins()
// send a transfer tx without specifying account number and sequence
res, body, _ := doTransferWithGasAccAuto(t, port, seed, name1, memo, pw, "200000", 1.0, false, false, fees)
res, body, _ := doTransferWithGasAccAuto(
t, port, seed, name1, memo, pw, addr, "200000", 1.0, false, true, fees,
)
require.Equal(t, http.StatusOK, res.StatusCode, body)
// query sender
@ -336,7 +223,7 @@ func TestCoinMultiSendGenerateOnly(t *testing.T) {
defer cleanup()
// generate only
res, body, _ := doTransferWithGas(t, port, seed, "", memo, "", addr, "200000", 1, false, true, fees)
res, body, _ := doTransferWithGas(t, port, seed, "", memo, "", addr, "200000", 1, false, false, fees)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var stdTx auth.StdTx
@ -356,6 +243,7 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) {
require.NoError(t, err)
addr, seed := CreateAddr(t, name1, pw, kb)
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, true)
defer cleanup()
acc := getAccount(t, port, addr)
@ -371,74 +259,40 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) {
// generate tx
gas := fmt.Sprintf("%d", gasEstResp.GasEstimate)
res, body, _ = doTransferWithGas(t, port, seed, name1, memo, "", addr, gas, 1, false, true, fees)
res, body, _ = doTransferWithGas(t, port, seed, name1, memo, "", addr, gas, 1, false, false, fees)
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].Route(), "bank")
require.Equal(t, msg.Msgs[0].GetSigners(), []sdk.AccAddress{addr})
require.Equal(t, 0, len(msg.Signatures))
require.Equal(t, memo, msg.Memo)
require.NotZero(t, msg.Fee.Gas)
var tx auth.StdTx
require.Nil(t, cdc.UnmarshalJSON([]byte(body), &tx))
require.Equal(t, len(tx.Msgs), 1)
require.Equal(t, tx.Msgs[0].Route(), "bank")
require.Equal(t, tx.Msgs[0].GetSigners(), []sdk.AccAddress{addr})
require.Equal(t, 0, len(tx.Signatures))
require.Equal(t, memo, tx.Memo)
require.NotZero(t, tx.Fee.Gas)
gasEstimate := int64(msg.Fee.Gas)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
// sign tx
var signedMsg auth.StdTx
payload := authrest.SignBody{
Tx: msg,
BaseReq: rest.NewBaseReq(
name1, pw, "", viper.GetString(client.FlagChainID), "", "",
accnum, sequence, nil, nil, false, false,
),
}
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"`
Return string `json:"return"`
}{Tx: signedMsg, Return: "block"}
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)
gasEstimate := int64(tx.Fee.Gas)
_, body = signAndBroadcastGenTx(t, port, name1, pw, body, acc, 1.0, false)
// check if tx was committed
var resultTx sdk.TxResponse
require.Nil(t, cdc.UnmarshalJSON([]byte(body), &resultTx))
require.Equal(t, uint32(0), resultTx.Code)
require.Equal(t, gasEstimate, resultTx.GasWanted)
var txResp sdk.TxResponse
require.Nil(t, cdc.UnmarshalJSON([]byte(body), &txResp))
require.Equal(t, uint32(0), txResp.Code)
require.Equal(t, gasEstimate, txResp.GasWanted)
}
func TestEncodeTx(t *testing.T) {
// Setup
kb, err := keys.NewKeyBaseFromDir(InitClientHome(t, ""))
require.NoError(t, err)
addr, seed := CreateAddr(t, name1, pw, kb)
cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, true)
defer cleanup()
// Make a transaction to test with
res, body, _ := doTransferWithGas(t, port, seed, name1, memo, "", addr, "2", 1, false, true, fees)
res, body, _ := doTransferWithGas(t, port, seed, name1, memo, "", addr, "2", 1, false, false, fees)
var tx auth.StdTx
cdc.UnmarshalJSON([]byte(body), &tx)
// Build the request
// build the request
encodeReq := struct {
Tx auth.StdTx `json:"tx"`
}{Tx: tx}
@ -446,20 +300,19 @@ func TestEncodeTx(t *testing.T) {
res, body = Request(t, port, "POST", "/tx/encode", encodedJSON)
// Make sure it came back ok, and that we can decode it back to the transaction
// 200 response
// 200 response.
require.Equal(t, http.StatusOK, res.StatusCode, body)
encodeResp := struct {
Tx string `json:"tx"`
}{}
// No error decoding the JSON
require.Nil(t, cdc.UnmarshalJSON([]byte(body), &encodeResp))
// Check that the base64 decodes
// verify that the base64 decodes
decodedBytes, err := base64.StdEncoding.DecodeString(encodeResp.Tx)
require.Nil(t, err)
// Check that the transaction decodes as expected
// check that the transaction decodes as expected
var decodedTx auth.StdTx
require.Nil(t, cdc.UnmarshalBinaryLengthPrefixed(decodedBytes, &decodedTx))
require.Equal(t, memo, decodedTx.Memo)

View File

@ -3,14 +3,12 @@ swagger: "2.0"
info:
version: "3.0"
title: Gaia-Lite for Cosmos
description: A REST interface for state queries, transaction generation, signing, and broadcast.
description: A REST interface for state queries, transaction generation and broadcasting.
tags:
- name: ICS0
description: Tendermint APIs, such as query blocks, transactions and validatorset
- name: ICS1
description: Key management APIs
- name: ICS20
description: Create, sign and broadcast transactions
description: Create and broadcast transactions
- name: ICS21
description: Stake module APIs
- name: ICS22
@ -269,42 +267,6 @@ paths:
$ref: "#/definitions/BroadcastTxCommitResult"
500:
description: Internal Server Error
/tx/sign:
post:
tags:
- ICS20
summary: Sign a Tx
description: Sign a Tx providing locally stored account and according password
consumes:
- application/json
produces:
- application/json
parameters:
- in: body
name: sendToken
description: sign tx
required: true
schema:
type: object
properties:
base_req:
$ref: "#/definitions/BaseReq"
tx:
$ref: "#/definitions/StdTx"
append_sig:
type: boolean
example: true
responses:
200:
description: The signed Tx
schema:
$ref: "#/definitions/StdTx"
400:
description: The Tx was malformated or key doesn't exist
401:
description: Key password is wrong
500:
description: Server internal error
/tx/broadcast:
post:
tags:
@ -393,8 +355,7 @@ paths:
description: Server internal error
/bank/accounts/{address}/transfers:
post:
summary: Send coins (build -> sign -> send)
description: Send coins (build -> sign -> send)
summary: Send coins from one account to another
tags:
- ICS20
consumes:
@ -409,7 +370,7 @@ paths:
type: string
- in: body
name: account
description: The password of the account to remove from the KMS
description: The sender and tx information
required: true
schema:
type: object
@ -422,188 +383,13 @@ paths:
$ref: "#/definitions/Coin"
responses:
202:
description: Tx was send and will probably be added to the next block
description: Tx was succesfully generated
schema:
$ref: "#/definitions/BroadcastTxCommitResult"
$ref: "#/definitions/StdTx"
400:
description: Invalid request
401:
description: Key password is wrong
500:
description: Server internal error
/keys:
get:
summary: List of accounts stored locally
tags:
- ICS1
produces:
- application/json
responses:
200:
description: Array of accounts
schema:
type: array
items:
$ref: "#/definitions/KeyOutput"
500:
description: Server internal error
post:
summary: Create a new account locally
tags:
- ICS1
consumes:
- application/json
produces:
- application/json
parameters:
- in: body
name: account
description: The account to create
schema:
type: object
required:
- name
- password
- seed
properties:
name:
type: string
password:
type: string
seed:
type: string
responses:
200:
description: Returns account information of the created key
schema:
$ref: "#/definitions/KeyOutput"
400:
description: Invalid request
409:
description: Key name confliction
500:
description: Server internal error
/keys/seed:
get:
summary: Create a new seed to create a new account with
tags:
- ICS1
responses:
200:
description: 24 word Seed
schema:
type: string
example: blossom pool issue kidney elevator blame furnace winter account merry vessel security depend exact travel bargain problem jelly rural net again mask roast chest
/keys/{name}/recover:
post:
summary: Recover a account from a seed
tags:
- ICS1
consumes:
- application/json
produces:
- application/json
parameters:
- in: path
name: name
description: Account name
required: true
type: string
- in: body
name: pwdAndSeed
description: Provide password and seed to recover a key
schema:
type: object
required:
- password
- seed
properties:
password:
type: string
seed:
type: string
responses:
200:
description: Returns account information of the recovered key
schema:
$ref: "#/definitions/KeyOutput"
400:
description: Invalid request
409:
description: Key name confliction
500:
description: Server internal error
/keys/{name}:
parameters:
- in: path
name: name
description: Account name
required: true
type: string
get:
summary: Get a certain locally stored account
tags:
- ICS1
produces:
- application/json
responses:
200:
description: Locally stored account
schema:
$ref: "#/definitions/KeyOutput"
404:
description: Key doesn't exist
put:
summary: Update the password for this account in the KMS
tags:
- ICS1
consumes:
- application/json
parameters:
- in: body
name: account
description: The new and old password
schema:
type: object
required:
- new_password
- old_password
properties:
new_password:
type: string
old_password:
type: string
responses:
200:
description: Updated password
401:
description: Key password is wrong
404:
description: Key doesn't exist
delete:
summary: Remove an account
tags:
- ICS1
consumes:
- application/json
parameters:
- in: body
name: account
description: The password of the account to remove from the KMS
schema:
type: object
required:
- password
properties:
password:
type: string
responses:
200:
description: Removed account
401:
description: Key password is wrong
404:
description: Key doesn't exist
/auth/accounts/{address}:
get:
summary: Get the account information on blockchain
@ -861,7 +647,7 @@ paths:
parameters:
- in: body
name: delegation
description: The password of the account to remove from the KMS
description: The sender and tx information
schema:
type: object
properties:
@ -884,13 +670,11 @@ paths:
- application/json
responses:
200:
description: OK
description: Tx was succesfully generated
schema:
$ref: "#/definitions/BroadcastTxCommitResult"
$ref: "#/definitions/StdTx"
400:
description: Invalid delegator address or redelegation request body
401:
description: Key password is wrong
500:
description: Internal Server Error
/staking/delegators/{delegatorAddr}/validators:
@ -1170,16 +954,14 @@ paths:
type: object
properties:
base_req:
$ref: "#/definitions/BaseReq"
$ref: "#/definitions/StdTx"
responses:
200:
description: OK
description: Tx was succesfully generated
schema:
$ref: "#/definitions/BroadcastTxCommitResult"
400:
description: Invalid validator address or base_req
401:
description: Key password is wrong
500:
description: Internal Server Error
/slashing/parameters:
@ -1246,13 +1028,11 @@ paths:
$ref: "#/definitions/Coin"
responses:
200:
description: OK
description: Tx was succesfully generated
schema:
$ref: "#/definitions/BroadcastTxCommitResult"
$ref: "#/definitions/StdTx"
400:
description: Invalid proposal body
401:
description: Key password is wrong
500:
description: Internal Server Error
get:
@ -2189,9 +1969,6 @@ definitions:
type: string
example: "cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc"
description: Sender address or Keybase name to generate a transaction
password:
type: string
example: "12345678"
memo:
type: string
example: "Sent via Cosmos Voyager 🚀"
@ -2214,10 +1991,6 @@ definitions:
type: array
items:
$ref: "#/definitions/Coin"
generate_only:
type: boolean
example: false
description: Create a JSON transaction that can be signed client side instead of actually signing and broadcasting
simulate:
type: boolean
example: false

View File

@ -20,6 +20,7 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/tx"
@ -397,7 +398,6 @@ func startLCD(logger log.Logger, listenAddr string, cdc *codec.Codec, t *testing
// NOTE: If making updates here also update cmd/gaia/cmd/gaiacli/main.go
func registerRoutes(rs *RestServer) {
keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent)
rpc.RegisterRoutes(rs.CliCtx, rs.Mux)
tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc)
authrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, auth.StoreKey)
@ -647,52 +647,42 @@ func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account {
// ICS 20 - Tokens
// ----------------------------------------------------------------------
// POST /tx/sign Sign a Tx
func doSign(t *testing.T, port, name, password, chainID string, accnum, sequence uint64, msg auth.StdTx) auth.StdTx {
var signedMsg auth.StdTx
payload := authrest.SignBody{
Tx: msg,
BaseReq: rest.NewBaseReq(
name, password, "", chainID, "", "", accnum, sequence, nil, nil, false, false,
),
}
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))
return signedMsg
}
// POST /tx/broadcast Send a signed Tx
func doBroadcast(t *testing.T, port string, msg auth.StdTx) sdk.TxResponse {
tx := authrest.BroadcastReq{Tx: msg, Return: "block"}
req, err := cdc.MarshalJSON(tx)
require.Nil(t, err)
res, body := Request(t, port, "POST", "/tx/broadcast", req)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var resultTx sdk.TxResponse
require.Nil(t, cdc.UnmarshalJSON([]byte(body), &resultTx))
return resultTx
}
func doBroadcast(t *testing.T, port string, tx auth.StdTx) (*http.Response, string) {
txReq := authrest.BroadcastReq{Tx: tx, Return: "block"}
// GET /bank/balances/{address} Get the account balances
// POST /bank/accounts/{address}/transfers Send coins (build -> sign -> send)
func doTransfer(t *testing.T, port, seed, name, memo, password string, addr sdk.AccAddress, fees sdk.Coins) (receiveAddr sdk.AccAddress, resultTx sdk.TxResponse) {
res, body, receiveAddr := doTransferWithGas(t, port, seed, name, memo, password, addr, "", 1.0, false, false, fees)
require.Equal(t, http.StatusOK, res.StatusCode, body)
err := cdc.UnmarshalJSON([]byte(body), &resultTx)
req, err := cdc.MarshalJSON(txReq)
require.Nil(t, err)
return receiveAddr, resultTx
return Request(t, port, "POST", "/tx/broadcast", req)
}
// doTransfer performs a balance transfer with auto gas calculation. It also signs
// the tx and broadcasts it.
func doTransfer(
t *testing.T, port, seed, name, memo, pwd string, addr sdk.AccAddress, fees sdk.Coins,
) (sdk.AccAddress, sdk.TxResponse) {
resp, body, recvAddr := doTransferWithGas(
t, port, seed, name, memo, pwd, addr, "", 1.0, false, true, fees,
)
require.Equal(t, http.StatusOK, resp.StatusCode, resp)
var txResp sdk.TxResponse
err := cdc.UnmarshalJSON([]byte(body), &txResp)
require.NoError(t, err)
return recvAddr, txResp
}
// doTransferWithGas performs a balance transfer with a specified gas value. The
// broadcast parameter determines if the tx should only be generated or also
// signed and broadcasted. The sending account's number and sequence are
// determined prior to generating the tx.
func doTransferWithGas(
t *testing.T, port, seed, from, memo, password string, addr sdk.AccAddress,
gas string, gasAdjustment float64, simulate, generateOnly bool, fees sdk.Coins,
) (res *http.Response, body string, receiveAddr sdk.AccAddress) {
t *testing.T, port, seed, name, memo, pwd string, addr sdk.AccAddress,
gas string, gasAdjustment float64, simulate, broadcast bool, fees sdk.Coins,
) (resp *http.Response, body string, receiveAddr sdk.AccAddress) {
// create receive address
kb := crkeys.NewInMemory()
@ -708,15 +698,9 @@ func doTransferWithGas(
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
if generateOnly {
// generate only txs do not use a Keybase so the address must be used
from = addr.String()
}
from := addr.String()
baseReq := rest.NewBaseReq(
from, password, memo, chainID, gas,
fmt.Sprintf("%f", gasAdjustment), accnum, sequence, fees, nil,
generateOnly, simulate,
from, memo, chainID, gas, fmt.Sprintf("%f", gasAdjustment), accnum, sequence, fees, nil, simulate,
)
sr := bankrest.SendReq{
@ -727,17 +711,28 @@ func doTransferWithGas(
req, err := cdc.MarshalJSON(sr)
require.NoError(t, err)
res, body = Request(t, port, "POST", fmt.Sprintf("/bank/accounts/%s/transfers", receiveAddr), req)
return
// generate tx
resp, body = Request(t, port, "POST", fmt.Sprintf("/bank/accounts/%s/transfers", receiveAddr), req)
if !broadcast {
return resp, body, receiveAddr
}
// sign and broadcast
resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, gasAdjustment, simulate)
return resp, body, receiveAddr
}
// doTransferWithGasAccAuto is similar to doTransferWithGas except that it
// automatically determines the account's number and sequence when generating the
// tx.
func doTransferWithGasAccAuto(
t *testing.T, port, seed, from, memo, password string, gas string,
gasAdjustment float64, simulate, generateOnly bool, fees sdk.Coins,
) (res *http.Response, body string, receiveAddr sdk.AccAddress) {
t *testing.T, port, seed, name, memo, pwd string, addr sdk.AccAddress,
gas string, gasAdjustment float64, simulate, broadcast bool, fees sdk.Coins,
) (resp *http.Response, body string, receiveAddr sdk.AccAddress) {
// create receive address
kb := crkeys.NewInMemory()
acc := getAccount(t, port, addr)
receiveInfo, _, err := kb.CreateMnemonic(
"receive_address", crkeys.English, gapp.DefaultKeyPass, crkeys.SigningAlgo("secp256k1"),
@ -747,9 +742,9 @@ func doTransferWithGasAccAuto(
receiveAddr = sdk.AccAddress(receiveInfo.GetPubKey().Address())
chainID := viper.GetString(client.FlagChainID)
from := addr.String()
baseReq := rest.NewBaseReq(
from, password, memo, chainID, gas,
fmt.Sprintf("%f", gasAdjustment), 0, 0, fees, nil, generateOnly, simulate,
from, memo, chainID, gas, fmt.Sprintf("%f", gasAdjustment), 0, 0, fees, nil, simulate,
)
sr := bankrest.SendReq{
@ -760,8 +755,45 @@ func doTransferWithGasAccAuto(
req, err := cdc.MarshalJSON(sr)
require.NoError(t, err)
res, body = Request(t, port, "POST", fmt.Sprintf("/bank/accounts/%s/transfers", receiveAddr), req)
return
resp, body = Request(t, port, "POST", fmt.Sprintf("/bank/accounts/%s/transfers", receiveAddr), req)
if !broadcast {
return resp, body, receiveAddr
}
// sign and broadcast
resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, gasAdjustment, simulate)
return resp, body, receiveAddr
}
// signAndBroadcastGenTx accepts a successfully generated unsigned tx, signs it,
// and broadcasts it.
func signAndBroadcastGenTx(
t *testing.T, port, name, pwd, genTx string, acc auth.Account, gasAdjustment float64, simulate bool,
) (resp *http.Response, body string) {
chainID := viper.GetString(client.FlagChainID)
var tx auth.StdTx
err := cdc.UnmarshalJSON([]byte(genTx), &tx)
require.Nil(t, err)
txbldr := txbuilder.NewTxBuilder(
utils.GetTxEncoder(cdc),
acc.GetAccountNumber(),
acc.GetSequence(),
tx.Fee.Gas,
gasAdjustment,
simulate,
chainID,
tx.Memo,
tx.Fee.Amount,
nil,
)
signedTx, err := txbldr.SignStdTx(name, pwd, tx, false)
require.NoError(t, err)
return doBroadcast(t, port, signedTx)
}
// ----------------------------------------------------------------------
@ -769,30 +801,40 @@ func doTransferWithGasAccAuto(
// ----------------------------------------------------------------------
// POST /staking/delegators/{delegatorAddr}/delegations Submit delegation
func doDelegate(t *testing.T, port, name, password string,
delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Int, fees sdk.Coins) (resultTx sdk.TxResponse) {
func doDelegate(
t *testing.T, port, name, pwd string, delAddr sdk.AccAddress,
valAddr sdk.ValAddress, amount sdk.Int, fees sdk.Coins,
) sdk.TxResponse {
acc := getAccount(t, port, delAddr)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false)
from := acc.GetAddress().String()
baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false)
msg := msgDelegationsInput{
BaseReq: baseReq,
DelegatorAddr: delAddr,
ValidatorAddr: valAddr,
Delegation: sdk.NewCoin(sdk.DefaultBondDenom, amount),
}
req, err := cdc.MarshalJSON(msg)
require.NoError(t, err)
res, body := Request(t, port, "POST", fmt.Sprintf("/staking/delegators/%s/delegations", delAddr.String()), req)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var result sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &result)
require.Nil(t, err)
resp, body := Request(t, port, "POST", fmt.Sprintf("/staking/delegators/%s/delegations", delAddr.String()), req)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
return result
// sign and broadcast
resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, client.DefaultGasAdjustment, false)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
var txResp sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &txResp)
require.NoError(t, err)
return txResp
}
type msgDelegationsInput struct {
@ -803,31 +845,39 @@ type msgDelegationsInput struct {
}
// POST /staking/delegators/{delegatorAddr}/delegations Submit delegation
func doUndelegate(t *testing.T, port, name, password string,
delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Int, fees sdk.Coins) (resultTx sdk.TxResponse) {
func doUndelegate(
t *testing.T, port, name, pwd string, delAddr sdk.AccAddress,
valAddr sdk.ValAddress, amount sdk.Int, fees sdk.Coins,
) sdk.TxResponse {
acc := getAccount(t, port, delAddr)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false)
from := acc.GetAddress().String()
baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false)
msg := msgUndelegateInput{
BaseReq: baseReq,
DelegatorAddr: delAddr,
ValidatorAddr: valAddr,
SharesAmount: sdk.NewDecFromInt(amount),
}
req, err := cdc.MarshalJSON(msg)
require.NoError(t, err)
res, body := Request(t, port, "POST", fmt.Sprintf("/staking/delegators/%s/unbonding_delegations", delAddr), req)
require.Equal(t, http.StatusOK, res.StatusCode, body)
resp, body := Request(t, port, "POST", fmt.Sprintf("/staking/delegators/%s/unbonding_delegations", delAddr), req)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
var result sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &result)
require.Nil(t, err)
resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, client.DefaultGasAdjustment, false)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
return result
var txResp sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &txResp)
require.NoError(t, err)
return txResp
}
type msgUndelegateInput struct {
@ -838,17 +888,18 @@ type msgUndelegateInput struct {
}
// POST /staking/delegators/{delegatorAddr}/delegations Submit delegation
func doBeginRedelegation(t *testing.T, port, name, password string,
delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress, amount sdk.Int,
fees sdk.Coins) (resultTx sdk.TxResponse) {
func doBeginRedelegation(
t *testing.T, port, name, pwd string, delAddr sdk.AccAddress, valSrcAddr,
valDstAddr sdk.ValAddress, amount sdk.Int, fees sdk.Coins,
) sdk.TxResponse {
acc := getAccount(t, port, delAddr)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false)
from := acc.GetAddress().String()
baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false)
msg := stakingrest.MsgBeginRedelegateInput{
BaseReq: baseReq,
DelegatorAddr: delAddr,
@ -856,17 +907,21 @@ func doBeginRedelegation(t *testing.T, port, name, password string,
ValidatorDstAddr: valDstAddr,
SharesAmount: sdk.NewDecFromInt(amount),
}
req, err := cdc.MarshalJSON(msg)
require.NoError(t, err)
res, body := Request(t, port, "POST", fmt.Sprintf("/staking/delegators/%s/redelegations", delAddr), req)
require.Equal(t, http.StatusOK, res.StatusCode, body)
resp, body := Request(t, port, "POST", fmt.Sprintf("/staking/delegators/%s/redelegations", delAddr), req)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
var result sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &result)
require.Nil(t, err)
resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, client.DefaultGasAdjustment, false)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
return result
var txResp sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &txResp)
require.NoError(t, err)
return txResp
}
type msgBeginRedelegateInput struct {
@ -1073,15 +1128,18 @@ func getStakingParams(t *testing.T, port string) staking.Params {
// ICS 22 - Gov
// ----------------------------------------------------------------------
// POST /gov/proposals Submit a proposal
func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress,
amount sdk.Int, fees sdk.Coins) (resultTx sdk.TxResponse) {
func doSubmitProposal(
t *testing.T, port, seed, name, pwd string, proposerAddr sdk.AccAddress,
amount sdk.Int, fees sdk.Coins,
) sdk.TxResponse {
acc := getAccount(t, port, proposerAddr)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false)
from := acc.GetAddress().String()
baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false)
pr := govrest.PostProposalReq{
Title: "Test",
Description: "test",
@ -1095,14 +1153,17 @@ func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerA
require.NoError(t, err)
// submitproposal
res, body := Request(t, port, "POST", "/gov/proposals", req)
require.Equal(t, http.StatusOK, res.StatusCode, body)
resp, body := Request(t, port, "POST", "/gov/proposals", req)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
var results sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &results)
require.Nil(t, err)
resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, client.DefaultGasAdjustment, false)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
return results
var txResp sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &txResp)
require.NoError(t, err)
return txResp
}
// GET /gov/proposals Query proposals
@ -1161,15 +1222,18 @@ func getProposalsFilterStatus(t *testing.T, port string, status gov.ProposalStat
}
// POST /gov/proposals/{proposalId}/deposits Deposit tokens to a proposal
func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID uint64,
amount sdk.Int, fees sdk.Coins) (resultTx sdk.TxResponse) {
func doDeposit(
t *testing.T, port, seed, name, pwd string, proposerAddr sdk.AccAddress,
proposalID uint64, amount sdk.Int, fees sdk.Coins,
) sdk.TxResponse {
acc := getAccount(t, port, proposerAddr)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false)
from := acc.GetAddress().String()
baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false)
dr := govrest.DepositReq{
Depositor: proposerAddr,
Amount: sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, amount)},
@ -1179,14 +1243,17 @@ func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk
req, err := cdc.MarshalJSON(dr)
require.NoError(t, err)
res, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/deposits", proposalID), req)
require.Equal(t, http.StatusOK, res.StatusCode, body)
resp, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/deposits", proposalID), req)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
var results sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &results)
require.Nil(t, err)
resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, client.DefaultGasAdjustment, false)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
return results
var txResp sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &txResp)
require.NoError(t, err)
return txResp
}
// GET /gov/proposals/{proposalId}/deposits Query deposits
@ -1210,14 +1277,19 @@ func getTally(t *testing.T, port string, proposalID uint64) gov.TallyResult {
}
// POST /gov/proposals/{proposalId}/votes Vote a proposal
func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID uint64, option string, fees sdk.Coins) (resultTx sdk.TxResponse) {
func doVote(
t *testing.T, port, seed, name, pwd string, proposerAddr sdk.AccAddress,
proposalID uint64, option string, fees sdk.Coins,
) sdk.TxResponse {
// get the account to get the sequence
acc := getAccount(t, port, proposerAddr)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false)
from := acc.GetAddress().String()
baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false)
vr := govrest.VoteReq{
Voter: proposerAddr,
Option: option,
@ -1227,14 +1299,17 @@ func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.Ac
req, err := cdc.MarshalJSON(vr)
require.NoError(t, err)
res, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/votes", proposalID), req)
require.Equal(t, http.StatusOK, res.StatusCode, body)
resp, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/votes", proposalID), req)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
var results sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &results)
require.Nil(t, err)
resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, client.DefaultGasAdjustment, false)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
return results
var txResp sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &txResp)
require.NoError(t, err)
return txResp
}
// GET /gov/proposals/{proposalId}/votes Query voters
@ -1339,24 +1414,32 @@ func getSigningInfo(t *testing.T, port string, validatorPubKey string) slashing.
// TODO: Test this functionality, it is not currently in any of the tests
// POST /slashing/validators/{validatorAddr}/unjail Unjail a jailed validator
func doUnjail(t *testing.T, port, seed, name, password string,
valAddr sdk.ValAddress, fees sdk.Coins) (resultTx sdk.TxResponse) {
chainID := viper.GetString(client.FlagChainID)
baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", 1, 1, fees, nil, false, false)
func doUnjail(
t *testing.T, port, seed, name, pwd string, valAddr sdk.ValAddress, fees sdk.Coins,
) sdk.TxResponse {
acc := getAccount(t, port, sdk.AccAddress(valAddr.Bytes()))
from := acc.GetAddress().String()
chainID := viper.GetString(client.FlagChainID)
baseReq := rest.NewBaseReq(from, "", chainID, "", "", 1, 1, fees, nil, false)
ur := slashingrest.UnjailReq{
BaseReq: baseReq,
}
req, err := cdc.MarshalJSON(ur)
require.NoError(t, err)
res, body := Request(t, port, "POST", fmt.Sprintf("/slashing/validators/%s/unjail", valAddr.String()), req)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var results sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &results)
require.Nil(t, err)
resp, body := Request(t, port, "POST", fmt.Sprintf("/slashing/validators/%s/unjail", valAddr.String()), req)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
return results
resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, client.DefaultGasAdjustment, false)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
var txResp sdk.TxResponse
err = cdc.UnmarshalJSON([]byte(body), &txResp)
require.NoError(t, err)
return txResp
}
type unjailReq struct {
@ -1366,27 +1449,34 @@ type unjailReq struct {
// ICS24 - fee distribution
// POST /distribution/delegators/{delgatorAddr}/rewards Withdraw delegator rewards
func doWithdrawDelegatorAllRewards(t *testing.T, port, seed, name, password string,
delegatorAddr sdk.AccAddress, fees sdk.Coins) (resultTx sdk.TxResponse) {
func doWithdrawDelegatorAllRewards(
t *testing.T, port, seed, name, pwd string, delegatorAddr sdk.AccAddress, fees sdk.Coins,
) sdk.TxResponse {
// get the account to get the sequence
acc := getAccount(t, port, delegatorAddr)
accnum := acc.GetAccountNumber()
sequence := acc.GetSequence()
chainID := viper.GetString(client.FlagChainID)
baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false)
from := acc.GetAddress().String()
baseReq := rest.NewBaseReq(from, "", chainID, "", "", accnum, sequence, fees, nil, false)
wr := struct {
BaseReq rest.BaseReq `json:"base_req"`
}{BaseReq: baseReq}
req := cdc.MustMarshalJSON(wr)
res, body := Request(t, port, "POST", fmt.Sprintf("/distribution/delegators/%s/rewards", delegatorAddr), req)
require.Equal(t, http.StatusOK, res.StatusCode, body)
var results sdk.TxResponse
cdc.MustUnmarshalJSON([]byte(body), &results)
resp, body := Request(t, port, "POST", fmt.Sprintf("/distribution/delegators/%s/rewards", delegatorAddr), req)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
return results
resp, body = signAndBroadcastGenTx(t, port, name, pwd, body, acc, client.DefaultGasAdjustment, false)
require.Equal(t, http.StatusOK, resp.StatusCode, body)
var txResp sdk.TxResponse
err := cdc.UnmarshalJSON([]byte(body), &txResp)
require.NoError(t, err)
return txResp
}
func mustParseDecCoins(dcstring string) sdk.DecCoins {

View File

@ -7,7 +7,6 @@ import (
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys/keyerror"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/auth"
@ -17,84 +16,6 @@ import (
//-----------------------------------------------------------------------------
// Building / Sending utilities
// CompleteAndBroadcastTxREST implements a utility function that facilitates
// sending a series of messages in a signed tx. In addition, it will handle
// tx gas simulation and estimation.
//
// NOTE: Also see CompleteAndBroadcastTxCLI.
func CompleteAndBroadcastTxREST(w http.ResponseWriter, cliCtx context.CLIContext,
baseReq rest.BaseReq, msgs []sdk.Msg, cdc *codec.Codec) {
gasAdj, ok := rest.ParseFloat64OrReturnBadRequest(w, baseReq.GasAdjustment, client.DefaultGasAdjustment)
if !ok {
return
}
simAndExec, gas, err := client.ParseGas(baseReq.Gas)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
// derive the from account address and name from the Keybase
fromAddress, fromName, err := context.GetFromFields(baseReq.From)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress)
txBldr := authtxb.NewTxBuilder(
utils.GetTxEncoder(cdc), baseReq.AccountNumber,
baseReq.Sequence, gas, gasAdj, baseReq.Simulate,
baseReq.ChainID, baseReq.Memo, baseReq.Fees, baseReq.GasPrices,
)
txBldr, err = utils.PrepareTxBuilder(txBldr, cliCtx)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
if baseReq.Simulate || simAndExec {
if gasAdj < 0 {
rest.WriteErrorResponse(w, http.StatusBadRequest, client.ErrInvalidGasAdjustment.Error())
return
}
txBldr, err = utils.EnrichWithGas(txBldr, cliCtx, msgs)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
if baseReq.Simulate {
rest.WriteSimulationResponse(w, cdc, txBldr.Gas())
return
}
}
txBytes, err := txBldr.BuildAndSign(cliCtx.GetFromName(), baseReq.Password, msgs)
if keyerror.IsErrKeyNotFound(err) {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
} else if keyerror.IsErrWrongPassword(err) {
rest.WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
return
} else if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
res, err := cliCtx.BroadcastTx(txBytes)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
rest.PostProcessResponse(w, cdc, res, cliCtx.Indent)
}
// WriteGenerateStdTxResponse writes response for the generate only mode.
func WriteGenerateStdTxResponse(w http.ResponseWriter, cdc *codec.Codec,
cliCtx context.CLIContext, br rest.BaseReq, msgs []sdk.Msg) {
@ -115,7 +36,7 @@ func WriteGenerateStdTxResponse(w http.ResponseWriter, cdc *codec.Codec,
br.Simulate, br.ChainID, br.Memo, br.Fees, br.GasPrices,
)
if simAndExec {
if br.Simulate || simAndExec {
if gasAdj < 0 {
rest.WriteErrorResponse(w, http.StatusBadRequest, client.ErrInvalidGasAdjustment.Error())
return
@ -126,6 +47,11 @@ func WriteGenerateStdTxResponse(w http.ResponseWriter, cdc *codec.Codec,
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
if br.Simulate {
rest.WriteSimulationResponse(w, cdc, txBldr.Gas())
return
}
}
stdMsg, err := txBldr.BuildSignMsg(msgs)

View File

@ -8,7 +8,7 @@ import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/tendermint/go-amino"
amino "github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/libs/common"
"github.com/cosmos/cosmos-sdk/client/context"
@ -40,8 +40,6 @@ func GenerateOrBroadcastMsgs(cliCtx context.CLIContext, txBldr authtxb.TxBuilder
// 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.
//
// NOTE: Also see CompleteAndBroadcastTxREST.
func CompleteAndBroadcastTxCLI(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) error {
txBldr, err := PrepareTxBuilder(txBldr, cliCtx)
if err != nil {

View File

@ -158,7 +158,6 @@ func txCmd(cdc *amino.Codec, mc []sdk.ModuleClients) *cobra.Command {
// NOTE: If making updates here you also need to update the test helper in client/lcd/test_helper.go
func registerRoutes(rs *lcd.RestServer) {
registerSwaggerUI(rs)
keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent)
rpc.RegisterRoutes(rs.CliCtx, rs.Mux)
tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc)
auth.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, at.StoreKey)

View File

@ -22,7 +22,6 @@ type GasEstimateResponse struct {
// that all share common "base" fields.
type BaseReq struct {
From string `json:"from"`
Password string `json:"password"`
Memo string `json:"memo"`
ChainID string `json:"chain_id"`
AccountNumber uint64 `json:"account_number"`
@ -31,19 +30,17 @@ type BaseReq struct {
GasPrices sdk.DecCoins `json:"gas_prices"`
Gas string `json:"gas"`
GasAdjustment string `json:"gas_adjustment"`
GenerateOnly bool `json:"generate_only"`
Simulate bool `json:"simulate"`
}
// NewBaseReq creates a new basic request instance and sanitizes its values
func NewBaseReq(
from, password, memo, chainID string, gas, gasAdjustment string,
accNumber, seq uint64, fees sdk.Coins, gasPrices sdk.DecCoins, genOnly, simulate bool,
from, memo, chainID string, gas, gasAdjustment string, accNumber, seq uint64,
fees sdk.Coins, gasPrices sdk.DecCoins, simulate bool,
) BaseReq {
return BaseReq{
From: strings.TrimSpace(from),
Password: password,
Memo: strings.TrimSpace(memo),
ChainID: strings.TrimSpace(chainID),
Fees: fees,
@ -52,7 +49,6 @@ func NewBaseReq(
GasAdjustment: strings.TrimSpace(gasAdjustment),
AccountNumber: accNumber,
Sequence: seq,
GenerateOnly: genOnly,
Simulate: simulate,
}
}
@ -60,8 +56,8 @@ func NewBaseReq(
// Sanitize performs basic sanitization on a BaseReq object.
func (br BaseReq) Sanitize() BaseReq {
return NewBaseReq(
br.From, br.Password, br.Memo, br.ChainID, br.Gas, br.GasAdjustment,
br.AccountNumber, br.Sequence, br.Fees, br.GasPrices, br.GenerateOnly, br.Simulate,
br.From, br.Memo, br.ChainID, br.Gas, br.GasAdjustment,
br.AccountNumber, br.Sequence, br.Fees, br.GasPrices, br.Simulate,
)
}
@ -69,12 +65,8 @@ func (br BaseReq) Sanitize() BaseReq {
// logic is needed, the implementing request handler should perform those
// checks manually.
func (br BaseReq) ValidateBasic(w http.ResponseWriter) bool {
if !br.GenerateOnly && !br.Simulate {
if !br.Simulate {
switch {
case len(br.Password) == 0:
WriteErrorResponse(w, http.StatusUnauthorized, "password required but not specified")
return false
case len(br.ChainID) == 0:
WriteErrorResponse(w, http.StatusUnauthorized, "chain-id required but not specified")
return false
@ -91,8 +83,8 @@ func (br BaseReq) ValidateBasic(w http.ResponseWriter) bool {
}
}
if len(br.From) == 0 {
WriteErrorResponse(w, http.StatusUnauthorized, "name or address required but not specified")
if _, err := sdk.AccAddressFromBech32(br.From); err != nil || len(br.From) == 0 {
WriteErrorResponse(w, http.StatusUnauthorized, fmt.Sprintf("invalid from address: %s", br.From))
return false
}

View File

@ -14,35 +14,27 @@ import (
type mockResponseWriter struct{}
func TestBaseReq_ValidateBasic(t *testing.T) {
func TestBaseReqValidateBasic(t *testing.T) {
fromAddr := "cosmos1cq0sxam6x4l0sv9yz3a2vlqhdhvt2k6jtgcse0"
tenstakes, err := types.ParseCoins("10stake")
require.NoError(t, err)
onestake, err := types.ParseDecCoins("1.0stake")
require.NoError(t, err)
req1 := NewBaseReq(
"nonempty", "nonempty", "", "nonempty", "", "",
0, 0, tenstakes, nil, false, false,
fromAddr, "", "nonempty", "", "", 0, 0, tenstakes, nil, false,
)
req2 := NewBaseReq(
"", "nonempty", "", "nonempty", "", "",
0, 0, tenstakes, nil, false, false,
"", "", "nonempty", "", "", 0, 0, tenstakes, nil, false,
)
req3 := NewBaseReq(
"nonempty", "", "", "nonempty", "", "",
0, 0, tenstakes, nil, false, false,
fromAddr, "", "", "", "", 0, 0, tenstakes, nil, false,
)
req4 := NewBaseReq(
"nonempty", "nonempty", "", "", "", "",
0, 0, tenstakes, nil, false, false,
fromAddr, "", "nonempty", "", "", 0, 0, tenstakes, onestake, false,
)
req5 := NewBaseReq(
"nonempty", "nonempty", "", "nonempty", "", "",
0, 0, tenstakes, onestake, false, false,
)
req6 := NewBaseReq(
"nonempty", "nonempty", "", "nonempty", "", "",
0, 0, types.Coins{}, types.DecCoins{}, false, false,
fromAddr, "", "nonempty", "", "", 0, 0, types.Coins{}, types.DecCoins{}, false,
)
tests := []struct {
@ -52,11 +44,10 @@ func TestBaseReq_ValidateBasic(t *testing.T) {
want bool
}{
{"ok", req1, httptest.NewRecorder(), true},
{"neither fees nor gasprices provided", req6, httptest.NewRecorder(), true},
{"neither fees nor gasprices provided", req5, httptest.NewRecorder(), true},
{"empty from", req2, httptest.NewRecorder(), false},
{"empty password", req3, httptest.NewRecorder(), false},
{"empty chain-id", req4, httptest.NewRecorder(), false},
{"fees and gasprices provided", req5, httptest.NewRecorder(), false},
{"empty chain-id", req3, httptest.NewRecorder(), false},
{"fees and gasprices provided", req4, httptest.NewRecorder(), false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View File

@ -31,10 +31,6 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec,
"/tx/encode",
EncodeTxRequestHandlerFn(cdc, cliCtx),
).Methods("POST")
r.HandleFunc(
"/tx/sign",
SignTxRequestHandlerFn(cdc, cliCtx),
).Methods("POST")
}
// query accountREST Handler

View File

@ -5,5 +5,5 @@ import "github.com/cosmos/cosmos-sdk/x/auth"
// BroadcastReq requests broadcasting a transaction
type BroadcastReq struct {
Tx auth.StdTx `json:"tx"`
Return string `json:"return"`
Return string `json:"return"` // TODO: Do we need this?
}

View File

@ -1,81 +0,0 @@
package rest
import (
"net/http"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/utils"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys/keyerror"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/auth"
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
)
// SignBody defines the properties of a sign request's body.
type SignBody struct {
Tx auth.StdTx `json:"tx"`
AppendSig bool `json:"append_sig"`
BaseReq rest.BaseReq `json:"base_req"`
}
// nolint: unparam
// sign tx REST handler
func SignTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var m SignBody
if !rest.ReadRESTReq(w, r, cdc, &m) {
return
}
if !m.BaseReq.ValidateBasic(w) {
return
}
// validate tx
// discard error if it's CodeNoSignatures as the tx comes with no signatures
if err := m.Tx.ValidateBasic(); err != nil && err.Code() != sdk.CodeNoSignatures {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
// derive the from account address and name from the Keybase
fromAddress, fromName, err := context.GetFromFields(m.BaseReq.From)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress)
txBldr := authtxb.NewTxBuilder(
utils.GetTxEncoder(cdc),
m.BaseReq.AccountNumber,
m.BaseReq.Sequence,
m.Tx.Fee.Gas,
1.0,
false,
m.BaseReq.ChainID,
m.Tx.GetMemo(),
m.Tx.Fee.Amount,
nil,
)
signedTx, err := txBldr.SignStdTx(cliCtx.GetFromName(), m.BaseReq.Password, m.Tx, m.AppendSig)
if keyerror.IsErrKeyNotFound(err) {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
} else if keyerror.IsErrWrongPassword(err) {
rest.WriteErrorResponse(w, http.StatusUnauthorized, err.Error())
return
} else if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
rest.PostProcessResponse(w, cdc, signedTx, cliCtx.Indent)
}
}

View File

@ -54,30 +54,13 @@ func SendRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIC
return
}
if req.BaseReq.GenerateOnly {
// When generate only is supplied, the from field must be a valid Bech32
// address.
fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
msg := bank.NewMsgSend(fromAddr, toAddr, req.Amount)
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
return
}
// derive the from account address and name from the Keybase
fromAddress, fromName, err := context.GetFromFields(req.BaseReq.From)
fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress)
msg := bank.NewMsgSend(cliCtx.GetFromAddress(), toAddr, req.Amount)
clientrest.CompleteAndBroadcastTxREST(w, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc)
msg := bank.NewMsgSend(fromAddr, toAddr, req.Amount)
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}

View File

@ -56,8 +56,10 @@ type (
)
// Withdraw delegator rewards
func withdrawDelegatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext,
queryRoute string) http.HandlerFunc {
func withdrawDelegatorRewardsHandlerFn(
cdc *codec.Codec, cliCtx context.CLIContext, queryRoute string,
) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req withdrawRewardsReq
if !rest.ReadRESTReq(w, r, cdc, &req) {
@ -81,12 +83,7 @@ func withdrawDelegatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIConte
return
}
if req.BaseReq.GenerateOnly {
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, msgs)
return
}
clientrest.CompleteAndBroadcastTxREST(w, cliCtx, req.BaseReq, msgs, cdc)
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, msgs)
}
}
@ -121,12 +118,7 @@ func withdrawDelegationRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLICont
return
}
if req.BaseReq.GenerateOnly {
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
return
}
clientrest.CompleteAndBroadcastTxREST(w, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc)
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}
@ -156,12 +148,7 @@ func setDelegatorWithdrawalAddrHandlerFn(cdc *codec.Codec, cliCtx context.CLICon
return
}
if req.BaseReq.GenerateOnly {
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
return
}
clientrest.CompleteAndBroadcastTxREST(w, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc)
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}
@ -192,12 +179,7 @@ func withdrawValidatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIConte
return
}
if req.BaseReq.GenerateOnly {
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, msgs)
return
}
clientrest.CompleteAndBroadcastTxREST(w, cliCtx, req.BaseReq, msgs, cdc)
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, msgs)
}
}

View File

@ -101,12 +101,7 @@ func postProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han
return
}
if req.BaseReq.GenerateOnly {
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
return
}
clientrest.CompleteAndBroadcastTxREST(w, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc)
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}
@ -143,12 +138,7 @@ func depositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerF
return
}
if req.BaseReq.GenerateOnly {
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
return
}
clientrest.CompleteAndBroadcastTxREST(w, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc)
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}
@ -191,12 +181,7 @@ func voteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc
return
}
if req.BaseReq.GenerateOnly {
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
return
}
clientrest.CompleteAndBroadcastTxREST(w, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc)
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}

View File

@ -48,28 +48,15 @@ func TransferRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.
return
}
var fromAddr sdk.AccAddress
if req.BaseReq.GenerateOnly {
// When generate only is supplied, the from field must be a valid Bech32
// address.
addr, err := sdk.AccAddressFromBech32(req.BaseReq.From)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
fromAddr = addr
}
packet := ibc.NewIBCPacket(fromAddr, to, req.Amount, req.BaseReq.ChainID, destChainID)
msg := ibc.IBCTransferMsg{IBCPacket: packet}
if req.BaseReq.GenerateOnly {
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
from, err := sdk.AccAddressFromBech32(req.BaseReq.From)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
clientrest.CompleteAndBroadcastTxREST(w, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc)
packet := ibc.NewIBCPacket(from, to, req.Amount, req.BaseReq.ChainID, destChainID)
msg := ibc.IBCTransferMsg{IBCPacket: packet}
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}

View File

@ -49,6 +49,17 @@ func unjailRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CL
return
}
fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
if !bytes.Equal(fromAddr, valAddr) {
rest.WriteErrorResponse(w, http.StatusUnauthorized, "must use own validator address")
return
}
msg := slashing.NewMsgUnjail(valAddr)
err = msg.ValidateBasic()
if err != nil {
@ -56,25 +67,6 @@ func unjailRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CL
return
}
if req.BaseReq.GenerateOnly {
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
return
}
// derive the from account address and name from the Keybase
fromAddress, fromName, err := context.GetFromFields(req.BaseReq.From)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress)
if !bytes.Equal(cliCtx.GetFromAddress(), valAddr) {
rest.WriteErrorResponse(w, http.StatusUnauthorized, "must use own validator address")
return
}
clientrest.CompleteAndBroadcastTxREST(w, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc)
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}

View File

@ -76,26 +76,18 @@ func postDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.
return
}
if req.BaseReq.GenerateOnly {
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
return
}
// derive the from account address and name from the Keybase
fromAddress, fromName, err := context.GetFromFields(req.BaseReq.From)
fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress)
if !bytes.Equal(cliCtx.GetFromAddress(), req.DelegatorAddr) {
if !bytes.Equal(fromAddr, req.DelegatorAddr) {
rest.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address")
return
}
clientrest.CompleteAndBroadcastTxREST(w, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc)
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}
@ -118,26 +110,18 @@ func postRedelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx contex
return
}
if req.BaseReq.GenerateOnly {
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
return
}
// derive the from account address and name from the Keybase
fromAddress, fromName, err := context.GetFromFields(req.BaseReq.From)
fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress)
if !bytes.Equal(cliCtx.GetFromAddress(), req.DelegatorAddr) {
if !bytes.Equal(fromAddr, req.DelegatorAddr) {
rest.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address")
return
}
clientrest.CompleteAndBroadcastTxREST(w, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc)
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}
@ -160,25 +144,17 @@ func postUnbondingDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx
return
}
if req.BaseReq.GenerateOnly {
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
return
}
// derive the from account address and name from the Keybase
fromAddress, fromName, err := context.GetFromFields(req.BaseReq.From)
fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From)
if err != nil {
rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress)
if !bytes.Equal(cliCtx.GetFromAddress(), req.DelegatorAddr) {
if !bytes.Equal(fromAddr, req.DelegatorAddr) {
rest.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address")
return
}
clientrest.CompleteAndBroadcastTxREST(w, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc)
clientrest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg})
}
}