Merge PR #2441: Add swagger-ui for key management
This commit is contained in:
commit
7c9048b591
|
@ -88,7 +88,7 @@ FEATURES
|
||||||
* [gaia-lite] [\#966](https://github.com/cosmos/cosmos-sdk/issues/966) Add support for `generate_only=true` query argument to generate offline unsigned transactions
|
* [gaia-lite] [\#966](https://github.com/cosmos/cosmos-sdk/issues/966) Add support for `generate_only=true` query argument to generate offline unsigned transactions
|
||||||
* [gaia-lite] [\#1953](https://github.com/cosmos/cosmos-sdk/issues/1953) Add /sign endpoint to sign transactions generated with `generate_only=true`.
|
* [gaia-lite] [\#1953](https://github.com/cosmos/cosmos-sdk/issues/1953) Add /sign endpoint to sign transactions generated with `generate_only=true`.
|
||||||
* [gaia-lite] [\#1954](https://github.com/cosmos/cosmos-sdk/issues/1954) Add /broadcast endpoint to broadcast transactions signed by the /sign endpoint.
|
* [gaia-lite] [\#1954](https://github.com/cosmos/cosmos-sdk/issues/1954) Add /broadcast endpoint to broadcast transactions signed by the /sign endpoint.
|
||||||
* [gaia-lite] [\#2113](https://github.com/cosmos/cosmos-sdk/issues/2113) Rename `/accounts/{address}/send` to `/bank/accounts/{address}/transfers`
|
* [gaia-lite] [\#2113](https://github.com/cosmos/cosmos-sdk/issues/2113) Rename `/accounts/{address}/send` to `/bank/accounts/{address}/transfers`, rename `/accounts/{address}` to `/auth/accounts/{address}`
|
||||||
|
|
||||||
* Gaia CLI (`gaiacli`)
|
* Gaia CLI (`gaiacli`)
|
||||||
* [cli] Cmds to query staking pool and params
|
* [cli] Cmds to query staking pool and params
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
@ -61,7 +60,7 @@ func runAddCmd(cmd *cobra.Command, args []string) error {
|
||||||
name = "inmemorykey"
|
name = "inmemorykey"
|
||||||
} else {
|
} else {
|
||||||
if len(args) != 1 || len(args[0]) == 0 {
|
if len(args) != 1 || len(args[0]) == 0 {
|
||||||
return errors.New("you must provide a name for the key")
|
return errMissingName()
|
||||||
}
|
}
|
||||||
name = args[0]
|
name = args[0]
|
||||||
kb, err = GetKeyBase()
|
kb, err = GetKeyBase()
|
||||||
|
@ -144,11 +143,16 @@ func printCreate(info keys.Info, seed string) {
|
||||||
if !viper.GetBool(flagNoBackup) {
|
if !viper.GetBool(flagNoBackup) {
|
||||||
out.Seed = seed
|
out.Seed = seed
|
||||||
}
|
}
|
||||||
json, err := MarshalJSON(out)
|
var jsonString []byte
|
||||||
|
if viper.GetBool(client.FlagIndentResponse) {
|
||||||
|
jsonString, err = cdc.MarshalJSONIndent(out, "", " ")
|
||||||
|
} else {
|
||||||
|
jsonString, err = cdc.MarshalJSON(out)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // really shouldn't happen...
|
panic(err) // really shouldn't happen...
|
||||||
}
|
}
|
||||||
fmt.Println(string(json))
|
fmt.Println(string(jsonString))
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("I can't speak: %s", output))
|
panic(fmt.Sprintf("I can't speak: %s", output))
|
||||||
}
|
}
|
||||||
|
@ -165,77 +169,78 @@ type NewKeyBody struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// add new key REST handler
|
// add new key REST handler
|
||||||
func AddNewKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
func AddNewKeyRequestHandler(indent bool) http.HandlerFunc {
|
||||||
var kb keys.Keybase
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
var m NewKeyBody
|
var kb keys.Keybase
|
||||||
|
var m NewKeyBody
|
||||||
|
|
||||||
kb, err := GetKeyBase()
|
kb, err := GetKeyBase()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
|
||||||
err = json.Unmarshal(body, &m)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if m.Name == "" {
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
w.Write([]byte("You have to specify a name for the locally stored account."))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if m.Password == "" {
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
w.Write([]byte("You have to specify a password for the locally stored account."))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if already exists
|
|
||||||
infos, err := kb.List()
|
|
||||||
for _, i := range infos {
|
|
||||||
if i.GetName() == m.Name {
|
|
||||||
w.WriteHeader(http.StatusConflict)
|
|
||||||
w.Write([]byte(fmt.Sprintf("Account with name %s already exists.", m.Name)))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// create account
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
seed := m.Seed
|
if err != nil {
|
||||||
if seed == "" {
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
seed = getSeed(keys.Secp256k1)
|
w.Write([]byte(err.Error()))
|
||||||
}
|
return
|
||||||
info, err := kb.CreateKey(m.Name, seed, m.Password)
|
}
|
||||||
if err != nil {
|
err = json.Unmarshal(body, &m)
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
if err != nil {
|
||||||
w.Write([]byte(err.Error()))
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
w.Write([]byte(err.Error()))
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
if m.Name == "" {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
err = errMissingName()
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if m.Password == "" {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
err = errMissingPassword()
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
keyOutput, err := Bech32KeyOutput(info)
|
// check if already exists
|
||||||
if err != nil {
|
infos, err := kb.List()
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
for _, info := range infos {
|
||||||
w.Write([]byte(err.Error()))
|
if info.GetName() == m.Name {
|
||||||
return
|
w.WriteHeader(http.StatusConflict)
|
||||||
|
err = errKeyNameConflict(m.Name)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create account
|
||||||
|
seed := m.Seed
|
||||||
|
if seed == "" {
|
||||||
|
seed = getSeed(keys.Secp256k1)
|
||||||
|
}
|
||||||
|
info, err := kb.CreateKey(m.Name, seed, m.Password)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
keyOutput, err := Bech32KeyOutput(info)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
keyOutput.Seed = seed
|
||||||
|
|
||||||
|
PostProcessResponse(w, cdc, keyOutput, indent)
|
||||||
}
|
}
|
||||||
|
|
||||||
keyOutput.Seed = seed
|
|
||||||
|
|
||||||
bz, err := json.Marshal(keyOutput)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Write(bz)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// function to just a new seed to display in the UI before actually persisting it in the keybase
|
// function to just a new seed to display in the UI before actually persisting it in the keybase
|
||||||
func getSeed(algo keys.SigningAlgo) string {
|
func getSeed(algo keys.SigningAlgo) string {
|
||||||
kb := client.MockKeyBase()
|
kb := client.MockKeyBase()
|
||||||
|
@ -258,3 +263,82 @@ func SeedRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
seed := getSeed(algo)
|
seed := getSeed(algo)
|
||||||
w.Write([]byte(seed))
|
w.Write([]byte(seed))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecoverKeyBody is recover key request REST body
|
||||||
|
type RecoverKeyBody struct {
|
||||||
|
Password string `json:"password"`
|
||||||
|
Seed string `json:"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 RecoverKeyBody
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = cdc.UnmarshalJSON(body, &m)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if name == "" {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
err = errMissingName()
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if m.Password == "" {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
err = errMissingPassword()
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if m.Seed == "" {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
err = errMissingSeed()
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
kb, err := GetKeyBase()
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// check if already exists
|
||||||
|
infos, err := kb.List()
|
||||||
|
for _, info := range infos {
|
||||||
|
if info.GetName() == name {
|
||||||
|
w.WriteHeader(http.StatusConflict)
|
||||||
|
err = errKeyNameConflict(name)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := kb.CreateKey(name, m.Seed, m.Password)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
keyOutput, err := Bech32KeyOutput(info)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
PostProcessResponse(w, cdc, keyOutput, indent)
|
||||||
|
}
|
||||||
|
}
|
|
@ -68,14 +68,14 @@ func DeleteKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
decoder := json.NewDecoder(r.Body)
|
decoder := json.NewDecoder(r.Body)
|
||||||
err := decoder.Decode(&m)
|
err := decoder.Decode(&m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(400)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
kb, err = GetKeyBase()
|
kb, err = GetKeyBase()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -83,10 +83,10 @@ func DeleteKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
// TODO handle error if key is not available or pass is wrong
|
// TODO handle error if key is not available or pass is wrong
|
||||||
err = kb.Delete(name, m.Password)
|
err = kb.Delete(name, m.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(200)
|
w.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package keys
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func errKeyNameConflict(name string) error {
|
||||||
|
return fmt.Errorf("acount with name %s already exists", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func errMissingName() error {
|
||||||
|
return fmt.Errorf("you have to specify a name for the locally stored account")
|
||||||
|
}
|
||||||
|
|
||||||
|
func errMissingPassword() error {
|
||||||
|
return fmt.Errorf("you have to specify a password for the locally stored account")
|
||||||
|
}
|
||||||
|
|
||||||
|
func errMissingSeed() error {
|
||||||
|
return fmt.Errorf("you have to specify seed for key recover")
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
package keys
|
package keys
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -35,35 +34,31 @@ func runListCmd(cmd *cobra.Command, args []string) error {
|
||||||
// REST
|
// REST
|
||||||
|
|
||||||
// query key list REST handler
|
// query key list REST handler
|
||||||
func QueryKeysRequestHandler(w http.ResponseWriter, r *http.Request) {
|
func QueryKeysRequestHandler(indent bool) http.HandlerFunc {
|
||||||
kb, err := GetKeyBase()
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
if err != nil {
|
kb, err := GetKeyBase()
|
||||||
w.WriteHeader(500)
|
if err != nil {
|
||||||
w.Write([]byte(err.Error()))
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
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 {
|
||||||
|
PostProcessResponse(w, cdc, "[]", indent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
keysOutput, err := Bech32KeysOutput(infos)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
PostProcessResponse(w, cdc, keysOutput, indent)
|
||||||
}
|
}
|
||||||
infos, err := kb.List()
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(500)
|
|
||||||
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 {
|
|
||||||
w.Write([]byte("[]"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
keysOutput, err := Bech32KeysOutput(infos)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(500)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
output, err := json.MarshalIndent(keysOutput, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(500)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.Write(output)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,11 +30,12 @@ func Commands() *cobra.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
// resgister REST routes
|
// resgister REST routes
|
||||||
func RegisterRoutes(r *mux.Router) {
|
func RegisterRoutes(r *mux.Router, indent bool) {
|
||||||
r.HandleFunc("/keys", QueryKeysRequestHandler).Methods("GET")
|
r.HandleFunc("/keys", QueryKeysRequestHandler(indent)).Methods("GET")
|
||||||
r.HandleFunc("/keys", AddNewKeyRequestHandler).Methods("POST")
|
r.HandleFunc("/keys", AddNewKeyRequestHandler(indent)).Methods("POST")
|
||||||
r.HandleFunc("/keys/seed", SeedRequestHandler).Methods("GET")
|
r.HandleFunc("/keys/seed", SeedRequestHandler).Methods("GET")
|
||||||
r.HandleFunc("/keys/{name}", GetKeyRequestHandler).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}", UpdateKeyRequestHandler).Methods("PUT")
|
||||||
r.HandleFunc("/keys/{name}", DeleteKeyRequestHandler).Methods("DELETE")
|
r.HandleFunc("/keys/{name}", DeleteKeyRequestHandler).Methods("DELETE")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package keys
|
package keys
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
@ -91,44 +90,39 @@ func getBechKeyOut(bechPrefix string) (bechKeyOutFn, error) {
|
||||||
// REST
|
// REST
|
||||||
|
|
||||||
// get key REST handler
|
// get key REST handler
|
||||||
func GetKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
func GetKeyRequestHandler(indent bool) http.HandlerFunc {
|
||||||
vars := mux.Vars(r)
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
name := vars["name"]
|
vars := mux.Vars(r)
|
||||||
bechPrefix := r.URL.Query().Get(FlagBechPrefix)
|
name := vars["name"]
|
||||||
|
bechPrefix := r.URL.Query().Get(FlagBechPrefix)
|
||||||
|
|
||||||
if bechPrefix == "" {
|
if bechPrefix == "" {
|
||||||
bechPrefix = "acc"
|
bechPrefix = "acc"
|
||||||
|
}
|
||||||
|
|
||||||
|
bechKeyOut, err := getBechKeyOut(bechPrefix)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
keyOutput, err := bechKeyOut(info)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
PostProcessResponse(w, cdc, keyOutput, indent)
|
||||||
}
|
}
|
||||||
|
|
||||||
bechKeyOut, err := getBechKeyOut(bechPrefix)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(400)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
w.WriteHeader(404)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
keyOutput, err := bechKeyOut(info)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(500)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
output, err := json.MarshalIndent(keyOutput, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(500)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Write(output)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,14 +69,14 @@ func UpdateKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
decoder := json.NewDecoder(r.Body)
|
decoder := json.NewDecoder(r.Body)
|
||||||
err := decoder.Decode(&m)
|
err := decoder.Decode(&m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(400)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
kb, err = GetKeyBase()
|
kb, err = GetKeyBase()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -86,10 +86,10 @@ func UpdateKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
// TODO check if account exists and if password is correct
|
// TODO check if account exists and if password is correct
|
||||||
err = kb.Update(name, m.OldPassword, getNewpass)
|
err = kb.Update(name, m.OldPassword, getNewpass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(401)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(200)
|
w.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@ import (
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"net/http"
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
)
|
)
|
||||||
|
|
||||||
// KeyDBName is the directory under root where we store the keys
|
// KeyDBName is the directory under root where we store the keys
|
||||||
|
@ -231,3 +233,26 @@ func printPubKey(info keys.Info, bechKeyOut bechKeyOutFn) {
|
||||||
|
|
||||||
fmt.Println(ko.PubKey)
|
fmt.Println(ko.PubKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PostProcessResponse performs post process for rest response
|
||||||
|
func PostProcessResponse(w http.ResponseWriter, cdc *codec.Codec, response interface{}, indent bool) {
|
||||||
|
var output []byte
|
||||||
|
switch response.(type) {
|
||||||
|
default:
|
||||||
|
var err error
|
||||||
|
if indent {
|
||||||
|
output, err = cdc.MarshalJSONIndent(response, "", " ")
|
||||||
|
} else {
|
||||||
|
output, err = cdc.MarshalJSON(response)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case []byte:
|
||||||
|
output = response.([]byte)
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Write(output)
|
||||||
|
}
|
||||||
|
|
|
@ -54,9 +54,13 @@ func TestKeys(t *testing.T) {
|
||||||
match := reg.MatchString(seed)
|
match := reg.MatchString(seed)
|
||||||
require.True(t, match, "Returned seed has wrong format", seed)
|
require.True(t, match, "Returned seed has wrong format", seed)
|
||||||
|
|
||||||
|
// recover key
|
||||||
|
recoverName := "test_recovername"
|
||||||
|
recoverPassword := "1234567890"
|
||||||
|
doRecoverKey(t, port, recoverName, recoverPassword, seed)
|
||||||
|
|
||||||
newName := "test_newname"
|
newName := "test_newname"
|
||||||
newPassword := "0987654321"
|
newPassword := "0987654321"
|
||||||
|
|
||||||
// add key
|
// add key
|
||||||
jsonStr := []byte(fmt.Sprintf(`{"name":"%s", "password":"%s", "seed":"%s"}`, newName, newPassword, seed))
|
jsonStr := []byte(fmt.Sprintf(`{"name":"%s", "password":"%s", "seed":"%s"}`, newName, newPassword, seed))
|
||||||
res, body = Request(t, port, "POST", "/keys", jsonStr)
|
res, body = Request(t, port, "POST", "/keys", jsonStr)
|
||||||
|
@ -78,7 +82,7 @@ func TestKeys(t *testing.T) {
|
||||||
// existing keys
|
// existing keys
|
||||||
res, body = Request(t, port, "GET", "/keys", nil)
|
res, body = Request(t, port, "GET", "/keys", nil)
|
||||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||||
var m [2]keys.KeyOutput
|
var m [3]keys.KeyOutput
|
||||||
err = cdc.UnmarshalJSON([]byte(body), &m)
|
err = cdc.UnmarshalJSON([]byte(body), &m)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
@ -243,7 +247,7 @@ func TestCoinSend(t *testing.T) {
|
||||||
someFakeAddr := sdk.AccAddress(bz)
|
someFakeAddr := sdk.AccAddress(bz)
|
||||||
|
|
||||||
// query empty
|
// query empty
|
||||||
res, body := Request(t, port, "GET", fmt.Sprintf("/accounts/%s", someFakeAddr), nil)
|
res, body := Request(t, port, "GET", fmt.Sprintf("/auth/accounts/%s", someFakeAddr), nil)
|
||||||
require.Equal(t, http.StatusNoContent, res.StatusCode, body)
|
require.Equal(t, http.StatusNoContent, res.StatusCode, body)
|
||||||
|
|
||||||
acc := getAccount(t, port, addr)
|
acc := getAccount(t, port, addr)
|
||||||
|
@ -802,7 +806,7 @@ func TestProposalsQuery(t *testing.T) {
|
||||||
//_____________________________________________________________________________
|
//_____________________________________________________________________________
|
||||||
// get the account to get the sequence
|
// get the account to get the sequence
|
||||||
func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account {
|
func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account {
|
||||||
res, body := Request(t, port, "GET", fmt.Sprintf("/accounts/%s", addr), nil)
|
res, body := Request(t, port, "GET", fmt.Sprintf("/auth/accounts/%s", addr), nil)
|
||||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||||
var acc auth.Account
|
var acc auth.Account
|
||||||
err := cdc.UnmarshalJSON([]byte(body), &acc)
|
err := cdc.UnmarshalJSON([]byte(body), &acc)
|
||||||
|
@ -856,6 +860,20 @@ func doSendWithGas(t *testing.T, port, seed, name, password string, addr sdk.Acc
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func doRecoverKey(t *testing.T, port, recoverName, recoverPassword, seed string) {
|
||||||
|
jsonStr := []byte(fmt.Sprintf(`{"password":"%s", "seed":"%s"}`, recoverPassword, seed))
|
||||||
|
res, body := Request(t, port, "POST", fmt.Sprintf("/keys/%s/recover", recoverName), jsonStr)
|
||||||
|
|
||||||
|
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||||
|
var resp keys.KeyOutput
|
||||||
|
err := codec.Cdc.UnmarshalJSON([]byte(body), &resp)
|
||||||
|
require.Nil(t, err, body)
|
||||||
|
|
||||||
|
addr1Bech32 := resp.Address
|
||||||
|
_, err = sdk.AccAddressFromBech32(addr1Bech32)
|
||||||
|
require.NoError(t, err, "Failed to return a correct bech32 address")
|
||||||
|
}
|
||||||
|
|
||||||
func doSend(t *testing.T, port, seed, name, password string, addr sdk.AccAddress) (receiveAddr sdk.AccAddress, resultTx ctypes.ResultBroadcastTxCommit) {
|
func doSend(t *testing.T, port, seed, name, password string, addr sdk.AccAddress) (receiveAddr sdk.AccAddress, resultTx ctypes.ResultBroadcastTxCommit) {
|
||||||
res, body, receiveAddr := doSendWithGas(t, port, seed, name, password, addr, "", 0, "")
|
res, body, receiveAddr := doSendWithGas(t, port, seed, name, password, addr, "", 0, "")
|
||||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||||
|
|
|
@ -145,7 +145,7 @@ func createHandler(cdc *codec.Codec) *mux.Router {
|
||||||
r.HandleFunc("/version", CLIVersionRequestHandler).Methods("GET")
|
r.HandleFunc("/version", CLIVersionRequestHandler).Methods("GET")
|
||||||
r.HandleFunc("/node_version", NodeVersionRequestHandler(cliCtx)).Methods("GET")
|
r.HandleFunc("/node_version", NodeVersionRequestHandler(cliCtx)).Methods("GET")
|
||||||
|
|
||||||
keys.RegisterRoutes(r)
|
keys.RegisterRoutes(r, cliCtx.Indent)
|
||||||
rpc.RegisterRoutes(cliCtx, r)
|
rpc.RegisterRoutes(cliCtx, r)
|
||||||
tx.RegisterRoutes(cliCtx, r, cdc)
|
tx.RegisterRoutes(cliCtx, r, cdc)
|
||||||
auth.RegisterRoutes(cliCtx, r, cdc, "acc")
|
auth.RegisterRoutes(cliCtx, r, cdc, "acc")
|
||||||
|
|
|
@ -346,6 +346,184 @@ paths:
|
||||||
$ref: "#/definitions/BroadcastTxCommitResult"
|
$ref: "#/definitions/BroadcastTxCommitResult"
|
||||||
400:
|
400:
|
||||||
description: The Tx was malformated
|
description: The Tx was malformated
|
||||||
|
/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/Account'
|
||||||
|
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/Account"
|
||||||
|
/keys/seed:
|
||||||
|
get:
|
||||||
|
summary: Create a new seed to create a new account with
|
||||||
|
tags:
|
||||||
|
- ICS1
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: 16 word Seed
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
/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/Account"
|
||||||
|
/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/Account"
|
||||||
|
404:
|
||||||
|
description: Account is not available
|
||||||
|
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: Password is wrong
|
||||||
|
404:
|
||||||
|
description: Account is not available
|
||||||
|
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: Password is wrong
|
||||||
|
404:
|
||||||
|
description: Account is not available
|
||||||
|
/auth/accounts/{address}:
|
||||||
|
get:
|
||||||
|
summary: Get the account information on blockchain
|
||||||
|
tags:
|
||||||
|
- ICS1
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: address
|
||||||
|
description: Account address
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Account information on the blockchain
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/AccountQueryResponse"
|
||||||
|
404:
|
||||||
|
description: Account is not available
|
||||||
|
|
||||||
definitions:
|
definitions:
|
||||||
CheckTxResult:
|
CheckTxResult:
|
||||||
|
@ -561,7 +739,35 @@ definitions:
|
||||||
address:
|
address:
|
||||||
$ref: "#/definitions/Address"
|
$ref: "#/definitions/Address"
|
||||||
pub_key:
|
pub_key:
|
||||||
$ref: "#/definitions/PubKey"
|
type: string
|
||||||
|
example: "cosmospub1addwnpepqfgv3pakxazq2fgs8tmmhmzsrs94fptl7kyztyxprjpf0mkus3h7cxqe70s"
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
example: local
|
||||||
|
seed:
|
||||||
|
type: string
|
||||||
|
AccountInfo:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
account_number:
|
||||||
|
type: string
|
||||||
|
address:
|
||||||
|
type: string
|
||||||
|
coins:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/definitions/Coin"
|
||||||
|
public_key:
|
||||||
|
type: string
|
||||||
|
sequence:
|
||||||
|
type: string
|
||||||
|
AccountQueryResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
value:
|
||||||
|
$ref: "#/definitions/AccountInfo"
|
||||||
BlockID:
|
BlockID:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
|
|
@ -17,7 +17,7 @@ import (
|
||||||
// register REST routes
|
// register REST routes
|
||||||
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, storeName string) {
|
func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, storeName string) {
|
||||||
r.HandleFunc(
|
r.HandleFunc(
|
||||||
"/accounts/{address}",
|
"/auth/accounts/{address}",
|
||||||
QueryAccountRequestHandlerFn(storeName, cdc, authcmd.GetAccountDecoder(cdc), cliCtx),
|
QueryAccountRequestHandlerFn(storeName, cdc, authcmd.GetAccountDecoder(cdc), cliCtx),
|
||||||
).Methods("GET")
|
).Methods("GET")
|
||||||
r.HandleFunc(
|
r.HandleFunc(
|
||||||
|
@ -36,7 +36,6 @@ func QueryAccountRequestHandlerFn(
|
||||||
decoder auth.AccountDecoder, cliCtx context.CLIContext,
|
decoder auth.AccountDecoder, cliCtx context.CLIContext,
|
||||||
) http.HandlerFunc {
|
) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
bech32addr := vars["address"]
|
bech32addr := vars["address"]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue