From 5c27c0fce24ef93ed9161012af01a6e11fd05f6e Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Mon, 12 Nov 2018 15:41:30 +0800 Subject: [PATCH 1/4] Refactor keys modules, add seed and recover rest api, keep consistent with cosmos --- Gopkg.lock | 5 +- client/bank/lcd/query.go | 39 ++++++ client/bank/lcd/rest.go | 7 +- client/keys/errors.go | 19 +++ client/keys/lcd/add.go | 208 ++++++++++++++++++++++-------- client/keys/lcd/list.go | 57 ++++---- client/keys/lcd/root.go | 10 +- client/keys/lcd/show.go | 48 +++---- client/keys/utils.go | 24 ++++ client/lcd/lcd.go | 3 +- client/lcd/swaggerui/swagger.yaml | 116 ++++++++++++++--- 11 files changed, 392 insertions(+), 144 deletions(-) create mode 100644 client/keys/errors.go diff --git a/Gopkg.lock b/Gopkg.lock index 537e8e980..1acce5a54 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -70,7 +70,7 @@ [[projects]] branch = "irisnet/v0.26.0-iris" - digest = "1:dc540b2f062c26d69b7821a1324ab7afaf04b8920da500e3378300d10e6db324" + digest = "1:06d4b3860be91d8d0bf1933758dda2ce0b5709506343c0656e033d2b69ffd776" name = "github.com/cosmos/cosmos-sdk" packages = [ "baseapp", @@ -903,6 +903,7 @@ analyzer-name = "dep" analyzer-version = 1 input-imports = [ + "github.com/bartekn/go-bip39", "github.com/bgentry/speakeasy", "github.com/cosmos/cosmos-sdk/baseapp", "github.com/cosmos/cosmos-sdk/client", @@ -912,6 +913,7 @@ "github.com/cosmos/cosmos-sdk/codec", "github.com/cosmos/cosmos-sdk/crypto", "github.com/cosmos/cosmos-sdk/crypto/keys", + "github.com/cosmos/cosmos-sdk/crypto/keys/hd", "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror", "github.com/cosmos/cosmos-sdk/server", "github.com/cosmos/cosmos-sdk/server/mock", @@ -953,6 +955,7 @@ "github.com/spf13/viper", "github.com/stretchr/testify/assert", "github.com/stretchr/testify/require", + "github.com/syndtr/goleveldb/leveldb/opt", "github.com/tendermint/go-amino", "github.com/tendermint/tendermint/abci/server", "github.com/tendermint/tendermint/abci/types", diff --git a/client/bank/lcd/query.go b/client/bank/lcd/query.go index 3d5405630..3bcd5ba08 100644 --- a/client/bank/lcd/query.go +++ b/client/bank/lcd/query.go @@ -13,6 +13,45 @@ import ( "github.com/irisnet/irishub/client/utils" ) +// query accountREST Handler +func QueryBalancesRequestHandlerFn( + storeName string, cdc *codec.Codec, + decoder auth.AccountDecoder, cliCtx context.CLIContext, +) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + vars := mux.Vars(r) + bech32addr := vars["address"] + + addr, err := sdk.AccAddressFromBech32(bech32addr) + if err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + res, err := cliCtx.QueryStore(auth.AddressStoreKey(addr), storeName) + if err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + // the query will return empty if there is no data for this account + if len(res) == 0 { + w.WriteHeader(http.StatusNoContent) + return + } + + // decode the value + account, err := decoder(res) + if err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + utils.PostProcessResponse(w, cdc, account.GetCoins(), cliCtx.Indent) + } +} + // QueryAccountRequestHandlerFn performs account information query func QueryAccountRequestHandlerFn(storeName string, cdc *codec.Codec, decoder auth.AccountDecoder, cliCtx context.CLIContext, diff --git a/client/bank/lcd/rest.go b/client/bank/lcd/rest.go index f5251aceb..ff54fdf6d 100644 --- a/client/bank/lcd/rest.go +++ b/client/bank/lcd/rest.go @@ -9,9 +9,12 @@ import ( // RegisterRoutes - Central function to define routes that get registered by the main application func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) { - r.HandleFunc("/bank/{address}/send", SendRequestHandlerFn(cdc, cliCtx)).Methods("POST") - r.HandleFunc("/bank/accounts/{address}", + r.HandleFunc("/auth/accounts/{address}", QueryAccountRequestHandlerFn("acc", cdc, authcmd.GetAccountDecoder(cdc), cliCtx)).Methods("GET") + + r.HandleFunc("/bank/balances/{address}", + QueryBalancesRequestHandlerFn("acc", cdc, authcmd.GetAccountDecoder(cdc), cliCtx)).Methods("GET") + r.HandleFunc("/bank/accounts/{address}/transfers", SendRequestHandlerFn(cdc, cliCtx)).Methods("POST") r.HandleFunc("/bank/coin/{coin-type}", QueryCoinTypeRequestHandlerFn(cdc, cliCtx)).Methods("GET") diff --git a/client/keys/errors.go b/client/keys/errors.go new file mode 100644 index 000000000..664461c70 --- /dev/null +++ b/client/keys/errors.go @@ -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") +} \ No newline at end of file diff --git a/client/keys/lcd/add.go b/client/keys/lcd/add.go index d315412b8..9f532ec0f 100644 --- a/client/keys/lcd/add.go +++ b/client/keys/lcd/add.go @@ -1,13 +1,14 @@ package keys import ( - "fmt" "net/http" cryptokeys "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/irisnet/irishub/client/keys" "github.com/irisnet/irishub/client/utils" + "io/ioutil" + "github.com/gorilla/mux" ) // NewKeyBody - the request body for create or recover new keys @@ -18,72 +19,70 @@ type NewKeyBody struct { } // AddNewKeyRequestHandler performs create or recover new keys operation -func AddNewKeyRequestHandler(w http.ResponseWriter, r *http.Request) { - var kb cryptokeys.Keybase - var m NewKeyBody - - kb, err := keys.GetKeyBase() - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } +func AddNewKeyRequestHandler(indent bool) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var kb cryptokeys.Keybase + var m NewKeyBody - err = utils.ReadPostBody(w, r, cdc, &m) - if err != nil { - return - } + kb, err := keys.GetKeyBase() + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + 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 - } + err = utils.ReadPostBody(w, r, cdc, &m) + if err != nil { + 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))) + if m.Name == "" { + w.WriteHeader(http.StatusBadRequest) + err = keys.ErrMissingName() + w.Write([]byte(err.Error())) + return + } + if m.Password == "" { + w.WriteHeader(http.StatusBadRequest) + err = keys.ErrMissingPassword() + w.Write([]byte(err.Error())) return } - } - // create account - seed := m.Seed - if seed == "" { - seed = getSeed(cryptokeys.Secp256k1) - } - info, err := kb.CreateKey(m.Name, seed, m.Password) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } + // check if already exists + infos, err := kb.List() + for _, i := range infos { + if i.GetName() == m.Name { + w.WriteHeader(http.StatusConflict) + err = keys.ErrKeyNameConflict(m.Name) + w.Write([]byte(err.Error())) + return + } + } - keyOutput, err := keys.Bech32KeyOutput(info) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } + // create account + seed := m.Seed + if seed == "" { + seed = getSeed(cryptokeys.Secp256k1) + } + info, err := kb.CreateKey(m.Name, seed, m.Password) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } - keyOutput.Seed = seed + keyOutput, err := keys.Bech32KeyOutput(info) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } - bz, err := cdc.MarshalJSONIndent(keyOutput,""," ") - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } + keyOutput.Seed = seed - w.Write(bz) + keys.PostProcessResponse(w, cdc, keyOutput, indent) + } } // function to just a new seed to display in the UI before actually persisting it in the keybase @@ -94,3 +93,98 @@ func getSeed(algo cryptokeys.SigningAlgo) string { _, seed, _ := kb.CreateMnemonic(name, cryptokeys.English, pass, algo) return seed } + +// 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 := cryptokeys.SigningAlgo(algoType) + + seed := getSeed(algo) + + w.Header().Set("Content-Type", "application/json") + 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 = keys.ErrMissingName() + w.Write([]byte(err.Error())) + return + } + if m.Password == "" { + w.WriteHeader(http.StatusBadRequest) + err = keys.ErrMissingPassword() + w.Write([]byte(err.Error())) + return + } + if m.Seed == "" { + w.WriteHeader(http.StatusBadRequest) + err = keys.ErrMissingSeed() + w.Write([]byte(err.Error())) + return + } + + kb, err := keys.GetKeyBaseWithWritePerm() + 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 = keys.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 := keys.Bech32KeyOutput(info) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } + + keys.PostProcessResponse(w, cdc, keyOutput, indent) + } +} diff --git a/client/keys/lcd/list.go b/client/keys/lcd/list.go index 5edc3a4f0..3bc5ad8c9 100644 --- a/client/keys/lcd/list.go +++ b/client/keys/lcd/list.go @@ -1,42 +1,37 @@ package keys import ( - "encoding/json" "net/http" "github.com/irisnet/irishub/client/keys" ) // query key list REST handler -func QueryKeysRequestHandler(w http.ResponseWriter, r *http.Request) { - kb, err := keys.GetKeyBase() - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return +func QueryKeysRequestHandler(indent bool) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + kb, err := keys.GetKeyBase() + 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 { + w.Write([]byte("[]")) + return + } + keysOutput, err := keys.Bech32KeysOutput(infos) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } + keys.PostProcessResponse(w, cdc, keysOutput, indent) } - 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 { - w.Write([]byte("[]")) - return - } - keysOutput, err := keys.Bech32KeysOutput(infos) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } - output, err := json.MarshalIndent(keysOutput, "", " ") - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } - w.Write(output) } diff --git a/client/keys/lcd/root.go b/client/keys/lcd/root.go index d9f7f592f..a02bb736d 100644 --- a/client/keys/lcd/root.go +++ b/client/keys/lcd/root.go @@ -5,10 +5,12 @@ import ( ) // resgister REST routes -func RegisterRoutes(r *mux.Router) { - r.HandleFunc("/keys", QueryKeysRequestHandler).Methods("GET") - r.HandleFunc("/keys", AddNewKeyRequestHandler).Methods("POST") - r.HandleFunc("/keys/{name}", GetKeyRequestHandler).Methods("GET") +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") } diff --git a/client/keys/lcd/show.go b/client/keys/lcd/show.go index c2f63a267..f8cba37fa 100644 --- a/client/keys/lcd/show.go +++ b/client/keys/lcd/show.go @@ -1,7 +1,6 @@ package keys import ( - "encoding/json" "fmt" "github.com/gorilla/mux" "github.com/irisnet/irishub/client/keys" @@ -13,35 +12,30 @@ import ( // REST // get key REST handler -func GetKeyRequestHandler(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - name := vars["name"] +func GetKeyRequestHandler(indent bool) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + name := vars["name"] - info, err := keys.GetKey(name) - if err != nil { - if strings.Contains(err.Error(), fmt.Sprintf("Key %s not found", name)) { - w.WriteHeader(http.StatusNotFound) - w.Write([]byte(err.Error())) - return - } else { + info, err := keys.GetKey(name) + if err != nil { + if strings.Contains(err.Error(), fmt.Sprintf("Key %s not found", name)) { + w.WriteHeader(http.StatusNotFound) + w.Write([]byte(err.Error())) + return + } else { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } + } + + keyOutput, err := keys.Bech32KeyOutput(info) + if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } + keys.PostProcessResponse(w, cdc, keyOutput, indent) } - - keyOutput, err := keys.Bech32KeyOutput(info) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } - output, err := json.MarshalIndent(keyOutput, "", " ") - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } - - w.Write(output) -} +} \ No newline at end of file diff --git a/client/keys/utils.go b/client/keys/utils.go index bee1f9155..4ca912a71 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -12,6 +12,7 @@ import ( "github.com/syndtr/goleveldb/leveldb/opt" "github.com/tendermint/tendermint/libs/cli" dbm "github.com/tendermint/tendermint/libs/db" + "net/http" ) // KeyDBName is the directory under root where we store the keys @@ -198,3 +199,26 @@ func PrintInfos(cdc *codec.Codec, infos []keys.Info) { func printKeyOutput(ko KeyOutput) { fmt.Printf("%s\t%s\t%s\t%s\n", ko.Name, ko.Type, ko.Address, 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) +} \ No newline at end of file diff --git a/client/lcd/lcd.go b/client/lcd/lcd.go index bcfe6e3aa..d4e5f1146 100644 --- a/client/lcd/lcd.go +++ b/client/lcd/lcd.go @@ -76,6 +76,7 @@ func ServeLCDStartCommand(cdc *codec.Codec) *cobra.Command { cmd.Flags().String(client.FlagNode, "tcp://localhost:26657", "Address of the node to connect to") cmd.Flags().Int(flagMaxOpenConnections, 1000, "The number of maximum open connections") cmd.Flags().Bool(client.FlagTrustNode, false, "Don't verify proofs for responses") + cmd.Flags().Bool(client.FlagIndentResponse, true, "Add indent to JSON response") return cmd } @@ -88,7 +89,7 @@ func createHandler(cdc *codec.Codec) *mux.Router { r.HandleFunc("/version", CLIVersionRequestHandler).Methods("GET") r.HandleFunc("/node_version", NodeVersionRequestHandler(cliCtx)).Methods("GET") - keyshandler.RegisterRoutes(r) + keyshandler.RegisterRoutes(r, cliCtx.Indent) bankhandler.RegisterRoutes(cliCtx, r, cdc) distributionhandler.RegisterRoutes(cliCtx, r, cdc) slashinghandler.RegisterRoutes(cliCtx, r, cdc) diff --git a/client/lcd/swaggerui/swagger.yaml b/client/lcd/swaggerui/swagger.yaml index effd118a0..97bc13dd3 100644 --- a/client/lcd/swaggerui/swagger.yaml +++ b/client/lcd/swaggerui/swagger.yaml @@ -466,7 +466,7 @@ paths: description: Not Found 500: description: Internal Server Error - /bank/accounts/{address}: + /bank/balances/{address}: get: summary: Get the account information on blockchain tags: @@ -483,30 +483,14 @@ paths: 200: description: Account information on the blockchain schema: - type: object - properties: - type: - type: string - value: - type: object - properties: - account_number: - type: string - address: - type: string - coins: - type: array - items: - $ref: "#/definitions/Coin" - public_key: - type: string - sequence: - type: string + type: array + items: + $ref: "#/definitions/Coin" 204: description: No content about this account address 500: description: Server internel error - /bank/{address}/send: + /bank/accounts/{address}/transfers: post: summary: Send coins (build -> sign -> send) description: Send coins (build -> sign -> send) @@ -615,6 +599,56 @@ paths: 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 @@ -686,6 +720,46 @@ paths: description: Key password is wrong 404: description: Key doesn't exist + /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: + type: object + properties: + type: + type: string + value: + type: object + properties: + account_number: + type: string + address: + type: string + coins: + type: array + items: + $ref: "#/definitions/Coin" + public_key: + type: string + sequence: + type: string + 204: + description: No content about this account address + 500: + description: Server internel error /stake/delegators/{delegatorAddr}/delegate: parameters: From b0945434862924dca1ecea5d7503ff091f74c0a6 Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Mon, 12 Nov 2018 15:42:25 +0800 Subject: [PATCH 2/4] Refactor tendermint api, remove hard code http error code --- client/tendermint/rpc/block.go | 24 ++++++++---------- client/tendermint/rpc/status.go | 36 ++++++++++++++------------- client/tendermint/rpc/validatorset.go | 23 ++++++++--------- client/tendermint/tx/querytx.go | 10 +++++--- client/tendermint/tx/searchtx.go | 26 ++++++++----------- 5 files changed, 59 insertions(+), 60 deletions(-) diff --git a/client/tendermint/rpc/block.go b/client/tendermint/rpc/block.go index 3099d5cb9..abedaf024 100644 --- a/client/tendermint/rpc/block.go +++ b/client/tendermint/rpc/block.go @@ -9,6 +9,7 @@ import ( tmliteProxy "github.com/tendermint/tendermint/lite/proxy" "net/http" "strconv" + "github.com/irisnet/irishub/client/utils" ) //BlockCommand returns the verified block data for a given heights @@ -58,13 +59,10 @@ func getBlock(cliCtx context.CLIContext, height *int64) ([]byte, error) { } } - // TODO move maarshalling into cmd/rest functions - // output, err := tmcodec.MarshalJSON(res) - output, err := cdc.MarshalJSONIndent(res, "", " ") - if err != nil { - return nil, err + if cliCtx.Indent { + return cdc.MarshalJSONIndent(res, "", " ") } - return output, nil + return cdc.MarshalJSON(res) } // get the current blockchain height @@ -111,23 +109,23 @@ func BlockRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { vars := mux.Vars(r) height, err := strconv.ParseInt(vars["height"], 10, 64) if err != nil { - w.WriteHeader(400) + w.WriteHeader(http.StatusBadRequest) w.Write([]byte("ERROR: Couldn't parse block height. Assumed format is '/block/{height}'.")) return } chainHeight, err := GetChainHeight(cliCtx) if height > chainHeight { - w.WriteHeader(404) + w.WriteHeader(http.StatusNotFound) w.Write([]byte("ERROR: Requested block height is bigger then the chain length.")) return } output, err := getBlock(cliCtx, &height) if err != nil { - w.WriteHeader(500) + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } - w.Write(output) + utils.PostProcessResponse(w, cdc, output, cliCtx.Indent) } } @@ -136,16 +134,16 @@ func LatestBlockRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { height, err := GetChainHeight(cliCtx) if err != nil { - w.WriteHeader(500) + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } output, err := getBlock(cliCtx, &height) if err != nil { - w.WriteHeader(500) + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } - w.Write(output) + utils.PostProcessResponse(w, cdc, output, cliCtx.Indent) } } diff --git a/client/tendermint/rpc/status.go b/client/tendermint/rpc/status.go index 7f00cd069..f46efdc8e 100644 --- a/client/tendermint/rpc/status.go +++ b/client/tendermint/rpc/status.go @@ -8,6 +8,8 @@ import ( ctypes "github.com/tendermint/tendermint/rpc/core/types" "net/http" "strconv" + "github.com/spf13/viper" + "github.com/irisnet/irishub/client/utils" ) func StatusCommand() *cobra.Command { @@ -22,7 +24,7 @@ func StatusCommand() *cobra.Command { return cmd } -func GetNodeStatus(cliCtx context.CLIContext) (*ctypes.ResultStatus, error) { +func getNodeStatus(cliCtx context.CLIContext) (*ctypes.ResultStatus, error) { // get the node node, err := cliCtx.GetNode() if err != nil { @@ -35,13 +37,20 @@ func GetNodeStatus(cliCtx context.CLIContext) (*ctypes.ResultStatus, error) { // CMD func printNodeStatus(cmd *cobra.Command, args []string) error { - status, err := GetNodeStatus(context.NewCLIContext()) + // No need to verify proof in getting node status + viper.Set(client.FlagTrustNode, true) + cliCtx := context.NewCLIContext() + status, err := getNodeStatus(cliCtx) if err != nil { return err } - output, err := cdc.MarshalJSON(status) - // output, err := cdc.MarshalJSONIndent(res, " ", "") + var output []byte + if cliCtx.Indent { + output, err = cdc.MarshalJSONIndent(status, "", " ") + } else { + output, err = cdc.MarshalJSON(status) + } if err != nil { return err } @@ -53,38 +62,31 @@ func printNodeStatus(cmd *cobra.Command, args []string) error { // REST handler for node info func NodeInfoRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - status, err := GetNodeStatus(cliCtx) + status, err := getNodeStatus(cliCtx) if err != nil { - w.WriteHeader(500) + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } nodeInfo := status.NodeInfo - output, err := cdc.MarshalJSONIndent(nodeInfo,"", " ") - if err != nil { - w.WriteHeader(500) - w.Write([]byte(err.Error())) - return - } - - w.Write(output) + utils.PostProcessResponse(w, cdc, nodeInfo, cliCtx.Indent) } } // REST handler for node syncing func NodeSyncingRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - status, err := GetNodeStatus(cliCtx) + status, err := getNodeStatus(cliCtx) if err != nil { - w.WriteHeader(500) + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } syncing := status.SyncInfo.CatchingUp if err != nil { - w.WriteHeader(500) + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } diff --git a/client/tendermint/rpc/validatorset.go b/client/tendermint/rpc/validatorset.go index 154266240..5efc194ae 100644 --- a/client/tendermint/rpc/validatorset.go +++ b/client/tendermint/rpc/validatorset.go @@ -13,6 +13,7 @@ import ( "github.com/irisnet/irishub/client/context" tmtypes "github.com/tendermint/tendermint/types" "net/http" + "github.com/irisnet/irishub/client/utils" ) // TODO these next two functions feel kinda hacky based on their placement @@ -95,12 +96,10 @@ func getValidators(cliCtx context.CLIContext, height *int64) ([]byte, error) { } } - output, err := cdc.MarshalJSONIndent(outputValidatorsRes, "", " ") - if err != nil { - return nil, err + if cliCtx.Indent { + return cdc.MarshalJSONIndent(outputValidatorsRes, "", " ") } - - return output, nil + return cdc.MarshalJSON(outputValidatorsRes) } // CMD @@ -135,26 +134,26 @@ func ValidatorSetRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { height, err := strconv.ParseInt(vars["height"], 10, 64) if err != nil { - w.WriteHeader(400) + w.WriteHeader(http.StatusBadRequest) w.Write([]byte("ERROR: Couldn't parse block height. Assumed format is '/validatorsets/{height}'.")) return } chainHeight, err := GetChainHeight(cliCtx) if height > chainHeight { - w.WriteHeader(404) + w.WriteHeader(http.StatusNotFound) w.Write([]byte("ERROR: Requested block height is bigger then the chain length.")) return } output, err := getValidators(cliCtx, &height) if err != nil { - w.WriteHeader(500) + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } - w.Write(output) + utils.PostProcessResponse(w, cdc, output, cliCtx.Indent) } } @@ -163,18 +162,18 @@ func LatestValidatorSetRequestHandlerFn(cliCtx context.CLIContext) http.HandlerF return func(w http.ResponseWriter, r *http.Request) { height, err := GetChainHeight(cliCtx) if err != nil { - w.WriteHeader(500) + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } output, err := getValidators(cliCtx, &height) if err != nil { - w.WriteHeader(500) + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } - w.Write(output) + utils.PostProcessResponse(w, cdc, output, cliCtx.Indent) } } diff --git a/client/tendermint/tx/querytx.go b/client/tendermint/tx/querytx.go index aee35d165..3cc9cb82a 100644 --- a/client/tendermint/tx/querytx.go +++ b/client/tendermint/tx/querytx.go @@ -14,6 +14,7 @@ import ( "github.com/tendermint/tendermint/libs/common" ctypes "github.com/tendermint/tendermint/rpc/core/types" "net/http" + "github.com/irisnet/irishub/client/utils" ) // QueryTxCmd implements the default command for a tx query. @@ -73,7 +74,10 @@ func queryTx(cdc *codec.Codec, cliCtx context.CLIContext, hashHexStr string) ([] return nil, err } - return cdc.MarshalJSONIndent(info, "", " ") + if cliCtx.Indent { + return cdc.MarshalJSONIndent(info, "", " ") + } + return cdc.MarshalJSON(info) } // ValidateTxResult performs transaction verification @@ -131,11 +135,11 @@ func QueryTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.H output, err := queryTx(cdc, cliCtx, hashHexStr) if err != nil { - w.WriteHeader(500) + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } - w.Write(output) + utils.PostProcessResponse(w, cdc, output, cliCtx.Indent) } } diff --git a/client/tendermint/tx/searchtx.go b/client/tendermint/tx/searchtx.go index 49531575d..449656d42 100644 --- a/client/tendermint/tx/searchtx.go +++ b/client/tendermint/tx/searchtx.go @@ -15,6 +15,7 @@ import ( ctypes "github.com/tendermint/tendermint/rpc/core/types" "net/http" "net/url" + "github.com/irisnet/irishub/client/utils" ) const ( @@ -50,9 +51,11 @@ $ iriscli tendermint txs --tag test1,test2 --any return err } - output, err := cdc.MarshalJSONIndent(txs, "", " ") - if err != nil { - return err + var output []byte + if cliCtx.Indent { + output, err = cdc.MarshalJSONIndent(txs, "", " ") + } else { + output, err = cdc.MarshalJSON(txs) } fmt.Println(string(output)) @@ -127,7 +130,7 @@ func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http. return func(w http.ResponseWriter, r *http.Request) { tag := r.FormValue("tag") if tag == "" { - w.WriteHeader(400) + w.WriteHeader(http.StatusBadRequest) w.Write([]byte("You need to provide at least a tag as a key=value pair to search for. Postfix the key with _bech32 to search bech32-encoded addresses or public keys")) return } @@ -137,7 +140,7 @@ func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http. value, err := url.QueryUnescape(keyValue[1]) if err != nil { - w.WriteHeader(400) + w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Could not decode address: " + err.Error())) return } @@ -147,7 +150,7 @@ func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http. prefix := strings.Split(bech32address, "1")[0] bz, err := sdk.GetFromBech32(bech32address, prefix) if err != nil { - w.WriteHeader(400) + w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } @@ -157,7 +160,7 @@ func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http. txs, err := searchTxs(cliCtx, cdc, []string{tag}) if err != nil { - w.WriteHeader(500) + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } @@ -167,13 +170,6 @@ func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http. return } - output, err := cdc.MarshalJSON(txs) - if err != nil { - w.WriteHeader(500) - w.Write([]byte(err.Error())) - return - } - - w.Write(output) + utils.PostProcessResponse(w, cdc, txs, cliCtx.Indent) } } From e8b39ec273de1eb6a7a96df8abcb4bda81228e28 Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Mon, 12 Nov 2018 16:55:34 +0800 Subject: [PATCH 3/4] Refactor keys api and normalize rest response --- client/bank/lcd/query.go | 22 ++---- client/distribution/lcd/query.go | 2 +- client/keys/cli/add.go | 2 +- client/keys/cli/delete.go | 11 +-- client/keys/cli/root.go | 2 +- client/keys/cli/show.go | 131 ++++++++++++++++++++++++++++--- client/keys/cli/update.go | 2 +- client/keys/lcd/show.go | 42 ++++++---- client/keys/utils.go | 99 +++++++++++++++++++---- client/slashing/lcd/query.go | 3 +- client/stake/lcd/query.go | 14 +--- 11 files changed, 250 insertions(+), 80 deletions(-) diff --git a/client/bank/lcd/query.go b/client/bank/lcd/query.go index 3bcd5ba08..54676e1ac 100644 --- a/client/bank/lcd/query.go +++ b/client/bank/lcd/query.go @@ -11,6 +11,7 @@ import ( "github.com/irisnet/irishub/client/bank" "github.com/irisnet/irishub/client/context" "github.com/irisnet/irishub/client/utils" + "strings" ) // query accountREST Handler @@ -91,14 +92,7 @@ func QueryAccountRequestHandlerFn(storeName string, cdc *codec.Codec, return } - // print out whole account - output, err := cdc.MarshalJSONIndent(accountRes, "", " ") - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("couldn't marshall query result. Error: %s", err.Error())) - return - } - - w.Write(output) + utils.PostProcessResponse(w, cdc, accountRes, cliCtx.Indent) } } @@ -109,16 +103,14 @@ func QueryCoinTypeRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext, vars := mux.Vars(r) coinType := vars["coin-type"] res, err := cliCtx.GetCoinType(coinType) - if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if strings.Contains(err.Error(),"unsupported coin type") { + w.WriteHeader(http.StatusNoContent) return - } - output, err := codec.MarshalJSONIndent(cdc, res) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + } else if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - w.Write(output) + utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } diff --git a/client/distribution/lcd/query.go b/client/distribution/lcd/query.go index db2a4e1a9..fa4477953 100644 --- a/client/distribution/lcd/query.go +++ b/client/distribution/lcd/query.go @@ -32,7 +32,7 @@ func QueryWithdrawAddressHandlerFn(storeName string, cliCtx context.CLIContext) return } if len(res) == 0 { - utils.WriteErrorResponse(w, http.StatusNoContent, "No withdraw address specified. If the delegator does have valid delegations, then the withdraw address should be the same as the delegator address") + utils.WriteErrorResponse(w, http.StatusNoContent, "") return } withdrawAddress := sdk.AccAddress(res) diff --git a/client/keys/cli/add.go b/client/keys/cli/add.go index f2f957a9f..d7881a8f9 100644 --- a/client/keys/cli/add.go +++ b/client/keys/cli/add.go @@ -125,7 +125,7 @@ func printCreate(info cryptokeys.Info, seed string) { output := viper.Get(cli.OutputFlag) switch output { case "text": - keys.PrintInfo(cdc, info) + keys.PrintKeyInfo(info, keys.Bech32KeyOutput) // print seed unless requested not to. if !viper.GetBool(client.FlagUseLedger) && !viper.GetBool(flagNoBackup) { fmt.Println("**Important** write this seed phrase in a safe place.") diff --git a/client/keys/cli/delete.go b/client/keys/cli/delete.go index eac6aa503..0f7286e05 100644 --- a/client/keys/cli/delete.go +++ b/client/keys/cli/delete.go @@ -2,17 +2,18 @@ package keys import ( "fmt" + "github.com/irisnet/irishub/client/keys" "github.com/spf13/cobra" ) func deleteKeyCommand() *cobra.Command { cmd := &cobra.Command{ - Use: "delete ", - Short: "Delete the given key", + Use: "delete ", + Short: "Delete the given key", Example: "iriscli keys delete ", - RunE: runDeleteCmd, - Args: cobra.ExactArgs(1), + RunE: runDeleteCmd, + Args: cobra.ExactArgs(1), } return cmd } @@ -20,7 +21,7 @@ func deleteKeyCommand() *cobra.Command { func runDeleteCmd(cmd *cobra.Command, args []string) error { name := args[0] - kb, err := keys.GetKeyBase() + kb, err := keys.GetKeyBaseWithWritePerm() if err != nil { return err } diff --git a/client/keys/cli/root.go b/client/keys/cli/root.go index f51ceba2b..060b09144 100644 --- a/client/keys/cli/root.go +++ b/client/keys/cli/root.go @@ -22,7 +22,7 @@ func Commands() *cobra.Command { newKeyCommand(), addKeyCommand(), listKeysCmd, - showKeysCmd, + showKeysCmd(), client.LineBreak, deleteKeyCommand(), updateKeyCommand(), diff --git a/client/keys/cli/show.go b/client/keys/cli/show.go index a67f6c0a6..2b688f630 100644 --- a/client/keys/cli/show.go +++ b/client/keys/cli/show.go @@ -1,22 +1,127 @@ package keys import ( - "github.com/irisnet/irishub/client/keys" + "fmt" + + cryptokeys "github.com/cosmos/cosmos-sdk/crypto/keys" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/multisig" + + "github.com/irisnet/irishub/client/keys" + "github.com/tendermint/tmlibs/cli" + +) + +const ( + // FlagAddress is the flag for the user's address on the command line. + FlagAddress = "address" + // FlagPublicKey represents the user's public key on the command line. + FlagPublicKey = "pubkey" + // FlagBechPrefix defines a desired Bech32 prefix encoding for a key. + FlagBechPrefix = "bech" + + flagMultiSigThreshold = "multisig-threshold" + defaultMultiSigKeyName = "multi" ) -var showKeysCmd = &cobra.Command{ - Use: "show ", - Short: "Show key info for the given name", - Long: `Return public details of one local key.`, - Example: "iriscli keys show ", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - name := args[0] - info, err := keys.GetKey(name) - if err == nil { - keys.PrintInfo(cdc, info) +var _ cryptokeys.Info = (*multiSigKey)(nil) + +type multiSigKey struct { + name string + key crypto.PubKey +} + +func (m multiSigKey) GetName() string { return m.name } +func (m multiSigKey) GetType() cryptokeys.KeyType { return cryptokeys.TypeLocal } +func (m multiSigKey) GetPubKey() crypto.PubKey { return m.key } +func (m multiSigKey) GetAddress() sdk.AccAddress { return sdk.AccAddress(m.key.Address()) } + +func showKeysCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "show [name]", + Short: "Show key info for the given name", + Long: `Return public details of one local key.`, + Args: cobra.MinimumNArgs(1), + RunE: runShowCmd, + } + + cmd.Flags().String(FlagBechPrefix, "acc", "The Bech32 prefix encoding for a key (acc|val|cons)") + cmd.Flags().Bool(FlagAddress, false, "output the address only (overrides --output)") + cmd.Flags().Bool(FlagPublicKey, false, "output the public key only (overrides --output)") + cmd.Flags().Uint(flagMultiSigThreshold, 1, "K out of N required signatures") + + return cmd +} + +func runShowCmd(cmd *cobra.Command, args []string) (err error) { + var info cryptokeys.Info + + if len(args) == 1 { + info, err = keys.GetKeyInfo(args[0]) + if err != nil { + return err } + } else { + pks := make([]crypto.PubKey, len(args)) + for i, keyName := range args { + info, err := keys.GetKeyInfo(keyName) + if err != nil { + return err + } + pks[i] = info.GetPubKey() + } + + multisigThreshold := viper.GetInt(flagMultiSigThreshold) + err = validateMultisigThreshold(multisigThreshold, len(args)) + if err != nil { + return err + } + multikey := multisig.NewPubKeyMultisigThreshold(multisigThreshold, pks) + info = multiSigKey{ + name: defaultMultiSigKeyName, + key: multikey, + } + } + + isShowAddr := viper.GetBool(FlagAddress) + isShowPubKey := viper.GetBool(FlagPublicKey) + isOutputSet := cmd.Flag(cli.OutputFlag).Changed + + if isShowAddr && isShowPubKey { + return fmt.Errorf("cannot use both --address and --pubkey at once") + } + + if isOutputSet && (isShowAddr || isShowPubKey) { + return fmt.Errorf("cannot use --output with --address or --pubkey") + } + + bechKeyOut, err := keys.GetBechKeyOut(viper.GetString(FlagBechPrefix)) + if err != nil { return err - }, + } + + switch { + case isShowAddr: + keys.PrintKeyAddress(info, bechKeyOut) + case isShowPubKey: + keys.PrintPubKey(info, bechKeyOut) + default: + keys.PrintKeyInfo(info, bechKeyOut) + } + + return nil +} + +func validateMultisigThreshold(k, nKeys int) error { + if k <= 0 { + return fmt.Errorf("threshold must be a positive integer") + } + if nKeys < k { + return fmt.Errorf( + "threshold k of n multisignature: %d < %d", nKeys, k) + } + return nil } diff --git a/client/keys/cli/update.go b/client/keys/cli/update.go index c1fc1df75..2510ce046 100644 --- a/client/keys/cli/update.go +++ b/client/keys/cli/update.go @@ -21,7 +21,7 @@ func runUpdateCmd(cmd *cobra.Command, args []string) error { name := args[0] buf := keys.BufferStdin() - kb, err := keys.GetKeyBase() + kb, err := keys.GetKeyBaseWithWritePerm() if err != nil { return err } diff --git a/client/keys/lcd/show.go b/client/keys/lcd/show.go index f8cba37fa..084ab5d94 100644 --- a/client/keys/lcd/show.go +++ b/client/keys/lcd/show.go @@ -1,11 +1,12 @@ package keys import ( - "fmt" + "net/http" + + "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" "github.com/gorilla/mux" "github.com/irisnet/irishub/client/keys" - "net/http" - "strings" + keycli "github.com/irisnet/irishub/client/keys/cli" ) /////////////////////////// @@ -16,26 +17,37 @@ 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(keycli.FlagBechPrefix) + + if bechPrefix == "" { + bechPrefix = "acc" + } - info, err := keys.GetKey(name) + bechKeyOut, err := keys.GetBechKeyOut(bechPrefix) if err != nil { - if strings.Contains(err.Error(), fmt.Sprintf("Key %s not found", name)) { - w.WriteHeader(http.StatusNotFound) - w.Write([]byte(err.Error())) - return - } else { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) - return - } + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return } - keyOutput, err := keys.Bech32KeyOutput(info) + info, err := keys.GetKeyInfo(name) + if keyerror.IsErrKeyNotFound(err) { + w.WriteHeader(http.StatusNoContent) + 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 } + keys.PostProcessResponse(w, cdc, keyOutput, indent) } -} \ No newline at end of file +} diff --git a/client/keys/utils.go b/client/keys/utils.go index 4ca912a71..b072ed581 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -18,6 +18,8 @@ import ( // KeyDBName is the directory under root where we store the keys const KeyDBName = "keys" +type BechKeyOutFn func(keyInfo keys.Info) (KeyOutput, error) + // keybase is used to make GetKeyBase a singleton var keybase keys.Keybase @@ -123,11 +125,11 @@ func GetKey(name string) (keys.Info, error) { // used for outputting keys.Info over REST type KeyOutput struct { - Name string `json:"name"` - Type string `json:"type"` - Address sdk.AccAddress `json:"address"` - PubKey string `json:"pub_key"` - Seed string `json:"seed,omitempty"` + Name string `json:"name"` + Type string `json:"type"` + Address string `json:"address"` + PubKey string `json:"pub_key"` + Seed string `json:"seed,omitempty"` } // create a list of KeyOutput in bech32 format @@ -143,35 +145,73 @@ func Bech32KeysOutput(infos []keys.Info) ([]KeyOutput, error) { return kos, nil } -// create a KeyOutput in bech32 format +// Bech32KeyOutput create a KeyOutput in bech32 format func Bech32KeyOutput(info keys.Info) (KeyOutput, error) { - account := sdk.AccAddress(info.GetPubKey().Address().Bytes()) + accAddr := sdk.AccAddress(info.GetPubKey().Address().Bytes()) bechPubKey, err := sdk.Bech32ifyAccPub(info.GetPubKey()) if err != nil { return KeyOutput{}, err } + return KeyOutput{ Name: info.GetName(), Type: info.GetType().String(), - Address: account, + Address: accAddr.String(), + PubKey: bechPubKey, + }, nil +} + +// Bech32ConsKeyOutput returns key output for a consensus node's key +// information. +func Bech32ConsKeyOutput(keyInfo keys.Info) (KeyOutput, error) { + consAddr := sdk.ConsAddress(keyInfo.GetPubKey().Address().Bytes()) + + bechPubKey, err := sdk.Bech32ifyConsPub(keyInfo.GetPubKey()) + if err != nil { + return KeyOutput{}, err + } + + return KeyOutput{ + Name: keyInfo.GetName(), + Type: keyInfo.GetType().String(), + Address: consAddr.String(), + PubKey: bechPubKey, + }, nil +} + +// Bech32ValKeyOutput returns key output for a validator's key information. +func Bech32ValKeyOutput(keyInfo keys.Info) (KeyOutput, error) { + valAddr := sdk.ValAddress(keyInfo.GetPubKey().Address().Bytes()) + + bechPubKey, err := sdk.Bech32ifyValPub(keyInfo.GetPubKey()) + if err != nil { + return KeyOutput{}, err + } + + return KeyOutput{ + Name: keyInfo.GetName(), + Type: keyInfo.GetType().String(), + Address: valAddr.String(), PubKey: bechPubKey, }, nil } -func PrintInfo(cdc *codec.Codec, info keys.Info) { - ko, err := Bech32KeyOutput(info) +func PrintKeyInfo(keyInfo keys.Info, bechKeyOut BechKeyOutFn) { + ko, err := bechKeyOut(keyInfo) if err != nil { panic(err) } + switch viper.Get(cli.OutputFlag) { case "text": fmt.Printf("NAME:\tTYPE:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n") - printKeyOutput(ko) + PrintKeyOutput(ko) case "json": - out, err := cdc.MarshalJSON(ko) + out, err := MarshalJSON(ko) if err != nil { panic(err) } + fmt.Println(string(out)) } } @@ -185,7 +225,7 @@ func PrintInfos(cdc *codec.Codec, infos []keys.Info) { case "text": fmt.Printf("NAME:\tTYPE:\tADDRESS:\t\t\t\t\t\tPUBKEY:\n") for _, ko := range kos { - printKeyOutput(ko) + PrintKeyOutput(ko) } case "json": out, err := cdc.MarshalJSON(kos) @@ -196,10 +236,41 @@ func PrintInfos(cdc *codec.Codec, infos []keys.Info) { } } -func printKeyOutput(ko KeyOutput) { +func PrintKeyOutput(ko KeyOutput) { fmt.Printf("%s\t%s\t%s\t%s\n", ko.Name, ko.Type, ko.Address, ko.PubKey) } +func PrintKeyAddress(info keys.Info, bechKeyOut BechKeyOutFn) { + ko, err := bechKeyOut(info) + if err != nil { + panic(err) + } + + fmt.Println(ko.Address) +} + +func PrintPubKey(info keys.Info, bechKeyOut BechKeyOutFn) { + ko, err := bechKeyOut(info) + if err != nil { + panic(err) + } + + fmt.Println(ko.PubKey) +} + +func GetBechKeyOut(bechPrefix string) (BechKeyOutFn, error) { + switch bechPrefix { + case "acc": + return Bech32KeyOutput, nil + case "val": + return Bech32ValKeyOutput, nil + case "cons": + return Bech32ConsKeyOutput, nil + } + + return nil, fmt.Errorf("invalid Bech32 prefix encoding provided: %s", bechPrefix) +} + // PostProcessResponse performs post process for rest response func PostProcessResponse(w http.ResponseWriter, cdc *codec.Codec, response interface{}, indent bool) { var output []byte diff --git a/client/slashing/lcd/query.go b/client/slashing/lcd/query.go index 7d42d92ff..a079cf2ff 100644 --- a/client/slashing/lcd/query.go +++ b/client/slashing/lcd/query.go @@ -30,8 +30,7 @@ func signingInfoHandlerFn(cliCtx context.CLIContext, storeName string, cdc *code return } if len(res) == 0 { - utils.WriteErrorResponse(w, http.StatusBadRequest, - fmt.Sprintf("the signing information of this validator %s is empty, please make sure its existence", vars["validator_pub"])) + utils.WriteErrorResponse(w, http.StatusNoContent, "") return } diff --git a/client/stake/lcd/query.go b/client/stake/lcd/query.go index b8a09c22d..67f09dfe7 100644 --- a/client/stake/lcd/query.go +++ b/client/stake/lcd/query.go @@ -181,12 +181,7 @@ func delegatorTxsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Han txs = append(txs, foundTxs...) } - res, err := cdc.MarshalJSON(txs) - if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + utils.PostProcessResponse(w, cdc, txs, cliCtx.Indent) } } @@ -231,12 +226,7 @@ func validatorsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Handl validatorOutputs[index] = validatorOutput } - if res, err = codec.MarshalJSONIndent(cdc, validatorOutputs); err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + utils.PostProcessResponse(w, cdc, validatorOutputs, cliCtx.Indent) } } From fb3dd77cdd20a3d1759dbf5d8d340e2e1fd5b0a6 Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Mon, 12 Nov 2018 17:00:07 +0800 Subject: [PATCH 4/4] Add chain-id option for testnet --- client/keys/lcd/add.go | 5 ++--- client/keys/utils.go | 4 ++-- client/lcd/lcd.go | 2 +- init/testnet.go | 10 ++++++++-- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/client/keys/lcd/add.go b/client/keys/lcd/add.go index 9f532ec0f..9e7356e03 100644 --- a/client/keys/lcd/add.go +++ b/client/keys/lcd/add.go @@ -1,14 +1,13 @@ package keys import ( + "io/ioutil" "net/http" cryptokeys "github.com/cosmos/cosmos-sdk/crypto/keys" - + "github.com/gorilla/mux" "github.com/irisnet/irishub/client/keys" "github.com/irisnet/irishub/client/utils" - "io/ioutil" - "github.com/gorilla/mux" ) // NewKeyBody - the request body for create or recover new keys diff --git a/client/keys/utils.go b/client/keys/utils.go index b072ed581..1d469a114 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -2,6 +2,7 @@ package keys import ( "fmt" + "net/http" "path/filepath" "github.com/cosmos/cosmos-sdk/client" @@ -12,7 +13,6 @@ import ( "github.com/syndtr/goleveldb/leveldb/opt" "github.com/tendermint/tendermint/libs/cli" dbm "github.com/tendermint/tendermint/libs/db" - "net/http" ) // KeyDBName is the directory under root where we store the keys @@ -292,4 +292,4 @@ func PostProcessResponse(w http.ResponseWriter, cdc *codec.Codec, response inter } w.Header().Set("Content-Type", "application/json") w.Write(output) -} \ No newline at end of file +} diff --git a/client/lcd/lcd.go b/client/lcd/lcd.go index d4e5f1146..69aada25d 100644 --- a/client/lcd/lcd.go +++ b/client/lcd/lcd.go @@ -72,7 +72,7 @@ func ServeLCDStartCommand(cdc *codec.Codec) *cobra.Command { cmd.Flags().String(flagListenAddr, "tcp://localhost:1317", "The address for the server to listen on") cmd.Flags().String(flagCORS, "", "Set the domains that can make CORS requests (* for all)") - cmd.Flags().String(client.FlagChainID, "", "The chain ID to connect to") + cmd.Flags().String(client.FlagChainID, "", "Chain ID of tendermint node") cmd.Flags().String(client.FlagNode, "tcp://localhost:26657", "Address of the node to connect to") cmd.Flags().Int(flagMaxOpenConnections, 1000, "The number of maximum open connections") cmd.Flags().Bool(client.FlagTrustNode, false, "Don't verify proofs for responses") diff --git a/init/testnet.go b/init/testnet.go index 41efd48b1..5fc79e5e1 100644 --- a/init/testnet.go +++ b/init/testnet.go @@ -33,6 +33,7 @@ var ( flagNodeDaemonHome = "node-daemon-home" flagNodeCliHome = "node-cli-home" flagStartingIPAddress = "starting-ip-address" + flagChainID = "chain-id" ) const nodeDirPerm = 0755 @@ -50,7 +51,7 @@ necessary files (private validator, genesis, config, etc.). Note, strict routability for addresses is turned off in the config file. Example: - iris testnet --v 4 --output-dir ./output --starting-ip-address 192.168.10.2 + iris testnet --v 4 --output-dir ./output --chain-id irishub-test --starting-ip-address 127.0.0.1 `, RunE: func(_ *cobra.Command, _ []string) error { config := ctx.Config @@ -58,6 +59,8 @@ Example: }, } + cmd.Flags().String(flagChainID, "", "Chain ID of tendermint node") + cmd.Flags().Int(flagNumValidators, 4, "Number of validators to initialize the testnet with", ) @@ -83,7 +86,10 @@ func initTestnet(config *cfg.Config, cdc *codec.Codec) error { outDir := viper.GetString(flagOutputDir) numValidators := viper.GetInt(flagNumValidators) - chainID := "chain-" + cmn.RandStr(6) + chainID := viper.GetString(flagChainID) + if chainID == "" { + chainID = "chain-" + cmn.RandStr(6) + } monikers := make([]string, numValidators) nodeIDs := make([]string, numValidators)