From f2664726823aa320be25c6ee5287f64242f291bc Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Thu, 23 Aug 2018 19:25:15 +0800 Subject: [PATCH 1/2] Refactor lcd according to code review --- client/context/context.go | 16 ++++++++-------- client/context/query.go | 8 ++++---- client/keys/add.go | 4 ++-- client/keys/delete.go | 10 ++++++++-- client/keys/list.go | 5 ++--- client/keys/update.go | 10 ++++++++-- client/lcd/root.go | 6 +++--- x/auth/client/rest/query.go | 2 +- x/bank/client/rest/sendtx.go | 7 +++---- 9 files changed, 39 insertions(+), 29 deletions(-) diff --git a/client/context/context.go b/client/context/context.go index 3e00abced534..76366a0a37c8 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -31,8 +31,8 @@ type CLIContext struct { Async bool JSON bool PrintResponse bool - Cert tendermintLite.Certifier - ClientMgr *ClientManager + Certifier tendermintLite.Certifier + ClientManager *ClientManager } // NewCLIContext returns a new initialized CLIContext with parameters from the @@ -117,14 +117,14 @@ func (ctx CLIContext) WithUseLedger(useLedger bool) CLIContext { return ctx } -// WithCert - return a copy of the context with an updated Cert -func (ctx CLIContext) WithCert(cert tendermintLite.Certifier) CLIContext { - ctx.Cert = cert +// WithCertifier - return a copy of the context with an updated Certifier +func (ctx CLIContext) WithCertifier(certifier tendermintLite.Certifier) CLIContext { + ctx.Certifier = certifier return ctx } -// WithClientMgr - return a copy of the context with an updated ClientMgr -func (ctx CLIContext) WithClientMgr(clientMgr *ClientManager) CLIContext { - ctx.ClientMgr = clientMgr +// WithClientManager - return a copy of the context with an updated ClientManager +func (ctx CLIContext) WithClientManager(clientManager *ClientManager) CLIContext { + ctx.ClientManager = clientManager return ctx } \ No newline at end of file diff --git a/client/context/query.go b/client/context/query.go index 77cdf5b3d88d..018462c9a93f 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -23,8 +23,8 @@ import ( // GetNode returns an RPC client. If the context's client is not defined, an // error is returned. func (ctx CLIContext) GetNode() (rpcclient.Client, error) { - if ctx.ClientMgr != nil { - return ctx.ClientMgr.getClient(), nil + if ctx.ClientManager != nil { + return ctx.ClientManager.getClient(), nil } if ctx.Client == nil { return nil, errors.New("no RPC client defined") @@ -314,7 +314,7 @@ func (ctx CLIContext) query(path string, key common.HexBytes) (res []byte, err e } // TODO: Later we consider to return error for missing valid certifier to verify data from untrusted node - if ctx.Cert == nil { + if ctx.Certifier == nil { if ctx.Logger != nil { io.WriteString(ctx.Logger, fmt.Sprintf("Missing valid certifier to verify data from untrusted node\n")) } @@ -322,7 +322,7 @@ func (ctx CLIContext) query(path string, key common.HexBytes) (res []byte, err e } // AppHash for height H is in header H+1 - commit, err := tendermintLiteProxy.GetCertifiedCommit(resp.Height+1, node, ctx.Cert) + commit, err := tendermintLiteProxy.GetCertifiedCommit(resp.Height+1, node, ctx.Certifier) if err != nil { return nil, err } diff --git a/client/keys/add.go b/client/keys/add.go index 904e17af0567..a0ba8cce86a0 100644 --- a/client/keys/add.go +++ b/client/keys/add.go @@ -180,7 +180,7 @@ func AddNewKeyRequestHandler(w http.ResponseWriter, r *http.Request) { } body, err := ioutil.ReadAll(r.Body) - err = json.Unmarshal(body, &m) + err = cdc.UnmarshalJSON(body, &m) if err != nil { w.WriteHeader(http.StatusBadRequest) @@ -248,7 +248,7 @@ func AddNewKeyRequest(gtx *gin.Context) { httputils.NewError(gtx, http.StatusBadRequest, err) return } - err = json.Unmarshal(body, &m) + err = cdc.UnmarshalJSON(body, &m) if err != nil { httputils.NewError(gtx, http.StatusBadRequest, err) return diff --git a/client/keys/delete.go b/client/keys/delete.go index 62cef116c7a4..722460f94726 100644 --- a/client/keys/delete.go +++ b/client/keys/delete.go @@ -12,6 +12,7 @@ import ( "github.com/spf13/cobra" "github.com/gin-gonic/gin" "github.com/cosmos/cosmos-sdk/client/httputils" + "io/ioutil" ) func deleteKeyCommand() *cobra.Command { @@ -96,10 +97,15 @@ func DeleteKeyRequestHandler(w http.ResponseWriter, r *http.Request) { // DeleteKeyRequest is the handler of deleting specified key in swagger rest server func DeleteKeyRequest(gtx *gin.Context) { name := gtx.Param("name") - var kb keys.Keybase var m DeleteKeyBody - if err := gtx.BindJSON(&m); err != nil { + body, err := ioutil.ReadAll(gtx.Request.Body) + if err != nil { + httputils.NewError(gtx, http.StatusBadRequest, err) + return + } + err = cdc.UnmarshalJSON(body, &m) + if err != nil { httputils.NewError(gtx, http.StatusBadRequest, err) return } diff --git a/client/keys/list.go b/client/keys/list.go index 934c0743f007..7cb4b2f65534 100644 --- a/client/keys/list.go +++ b/client/keys/list.go @@ -1,7 +1,6 @@ package keys import ( - "encoding/json" "net/http" "github.com/spf13/cobra" @@ -61,7 +60,7 @@ func QueryKeysRequestHandler(w http.ResponseWriter, r *http.Request) { w.Write([]byte(err.Error())) return } - output, err := json.MarshalIndent(keysOutput, "", " ") + output, err := cdc.MarshalJSONIndent(keysOutput, "", " ") if err != nil { w.WriteHeader(500) w.Write([]byte(err.Error())) @@ -92,7 +91,7 @@ func QueryKeysRequest(gtx *gin.Context) { httputils.NewError(gtx, http.StatusInternalServerError, err) return } - output, err := json.MarshalIndent(keysOutput, "", " ") + output, err := cdc.MarshalJSONIndent(keysOutput, "", " ") if err != nil { httputils.NewError(gtx, http.StatusInternalServerError, err) return diff --git a/client/keys/update.go b/client/keys/update.go index 3f7ab7c0944e..f731bc2216d3 100644 --- a/client/keys/update.go +++ b/client/keys/update.go @@ -13,6 +13,7 @@ import ( "github.com/gin-gonic/gin" "errors" "github.com/cosmos/cosmos-sdk/client/httputils" + "io/ioutil" ) func updateKeyCommand() *cobra.Command { @@ -100,10 +101,15 @@ func UpdateKeyRequestHandler(w http.ResponseWriter, r *http.Request) { // UpdateKeyRequest is the handler of updating specified key in swagger rest server func UpdateKeyRequest(gtx *gin.Context) { name := gtx.Param("name") - var kb keys.Keybase var m UpdateKeyBody - if err := gtx.BindJSON(&m); err != nil { + body, err := ioutil.ReadAll(gtx.Request.Body) + if err != nil { + httputils.NewError(gtx, http.StatusBadRequest, err) + return + } + err = cdc.UnmarshalJSON(body, &m) + if err != nil { httputils.NewError(gtx, http.StatusBadRequest, err) return } diff --git a/client/lcd/root.go b/client/lcd/root.go index 23488c05aee0..da0e14c8dac6 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -157,17 +157,17 @@ func createSwaggerHandler(server *gin.Engine, cdc *wire.Codec) { panic(fmt.Errorf("missing node URIs")) } //Tendermint certifier can only connect to one full node. Here we assign the first full node to it - cert,err := tendermintLiteProxy.GetCertifier(chainID, rootDir, nodeAddrArray[0]) + certifier, err := tendermintLiteProxy.GetCertifier(chainID, rootDir, nodeAddrArray[0]) if err != nil { panic(err) } //Create load balancing engine - clientMgr,err := context.NewClientManager(nodeAddrs) + clientManager, err := context.NewClientManager(nodeAddrs) if err != nil { panic(err) } //Assign tendermint certifier and load balancing engine to ctx - ctx := context.NewCLIContext().WithCodec(cdc).WithLogger(os.Stdout).WithCert(cert).WithClientMgr(clientMgr) + ctx := context.NewCLIContext().WithCodec(cdc).WithLogger(os.Stdout).WithCertifier(certifier).WithClientManager(clientManager) server.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) diff --git a/x/auth/client/rest/query.go b/x/auth/client/rest/query.go index aae2a1da2a41..f350358b6cfc 100644 --- a/x/auth/client/rest/query.go +++ b/x/auth/client/rest/query.go @@ -74,7 +74,7 @@ func QueryAccountRequestHandlerFn( // RegisterSwaggerRoutes - Central function to define account query related routes that get registered by the main application func RegisterSwaggerRoutes(routerGroup *gin.RouterGroup, ctx context.CLIContext, cdc *wire.Codec, storeName string) { - routerGroup.GET("accounts/:address",queryAccountRequestHandler(storeName,cdc,authcmd.GetAccountDecoder(cdc),ctx)) + routerGroup.GET("bank/balance/:address",queryAccountRequestHandler(storeName,cdc,authcmd.GetAccountDecoder(cdc),ctx)) } func queryAccountRequestHandler(storeName string, cdc *wire.Codec, decoder auth.AccountDecoder, ctx context.CLIContext) gin.HandlerFunc { diff --git a/x/bank/client/rest/sendtx.go b/x/bank/client/rest/sendtx.go index 554155f3daac..cfc286f4cf77 100644 --- a/x/bank/client/rest/sendtx.go +++ b/x/bank/client/rest/sendtx.go @@ -27,11 +27,9 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Codec, k } type sendBody struct { - // fees is not used currently - // Fees sdk.Coin `json="fees"` - Amount sdk.Coins `json:"amount"` - LocalAccountName string `json:"name"` + Name string `json:"name"` Password string `json:"password"` + Amount sdk.Coins `json:"amount"` ChainID string `json:"chain_id"` AccountNumber int64 `json:"account_number"` Sequence int64 `json:"sequence"` @@ -146,6 +144,7 @@ func RegisterSwaggerRoutes(routerGroup *gin.RouterGroup, ctx context.CLIContext, routerGroup.POST("/accounts/:address/send", sendRequestFn(cdc, ctx, kb)) routerGroup.POST("/create_transfer", createTransferTxForSignFn(cdc, ctx)) routerGroup.POST("/signed_transfer", composeAndBroadcastSignedTransferTxFn(cdc, ctx)) + routerGroup.POST("/bank/transfers", composeAndBroadcastSignedTransferTxFn(cdc, ctx)) } func composeTx(cdc *wire.Codec, ctx context.CLIContext, transferBody transferBody) (auth.StdSignMsg, authctx.TxContext, sdk.Error) { From 6cdf21ddc5a9ab5fcd8bbbac89b6a12ceacf7c04 Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Fri, 24 Aug 2018 10:56:43 +0800 Subject: [PATCH 2/2] Refactor code according to code review --- client/context/query.go | 72 ++++---- client/keys/add.go | 42 ++--- client/keys/list.go | 2 +- client/lcd/docs/docs.go | 175 ++------------------ client/lcd/root.go | 6 +- x/bank/client/rest/sendtx.go | 310 +++++++++++++---------------------- 6 files changed, 195 insertions(+), 412 deletions(-) diff --git a/client/context/query.go b/client/context/query.go index 018462c9a93f..71b16f710200 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -18,6 +18,7 @@ import ( "github.com/cosmos/cosmos-sdk/wire" "strings" tendermintLiteProxy "github.com/tendermint/tendermint/lite/proxy" + abci "github.com/tendermint/tendermint/abci/types" ) // GetNode returns an RPC client. If the context's client is not defined, an @@ -284,33 +285,11 @@ func (ctx CLIContext) ensureBroadcastTx(txBytes []byte) error { return nil } -// nolint: gocyclo -// query performs a query from a Tendermint node with the provided store name -// and path. -func (ctx CLIContext) query(path string, key common.HexBytes) (res []byte, err error) { - node, err := ctx.GetNode() - if err != nil { - return res, err - } - - opts := rpcclient.ABCIQueryOptions{ - Height: ctx.Height, - Trusted: ctx.TrustNode, - } - - result, err := node.ABCIQueryWithOptions(path, key, opts) - if err != nil { - return res, err - } - - resp := result.Response - if resp.Code != uint32(0) { - return res, errors.Errorf("query failed: (%d) %s", resp.Code, resp.Log) - } - - // Data from trusted node or subspace doesn't need verification +// proofVerify perform response proof verification +func (ctx CLIContext) proofVerify(path string, resp abci.ResponseQuery) error { + // Data from trusted node or subspace query doesn't need verification if ctx.TrustNode || !isQueryStoreWithProof(path) { - return resp.Value,nil + return nil } // TODO: Later we consider to return error for missing valid certifier to verify data from untrusted node @@ -318,30 +297,61 @@ func (ctx CLIContext) query(path string, key common.HexBytes) (res []byte, err e if ctx.Logger != nil { io.WriteString(ctx.Logger, fmt.Sprintf("Missing valid certifier to verify data from untrusted node\n")) } - return resp.Value, nil + return nil } + node, err := ctx.GetNode() // AppHash for height H is in header H+1 commit, err := tendermintLiteProxy.GetCertifiedCommit(resp.Height+1, node, ctx.Certifier) if err != nil { - return nil, err + return err } var multiStoreProof store.MultiStoreProof cdc := wire.NewCodec() err = cdc.UnmarshalBinary(resp.Proof, &multiStoreProof) if err != nil { - return res, errors.Wrap(err, "failed to unmarshalBinary rangeProof") + return errors.Wrap(err, "failed to unmarshalBinary rangeProof") } // Validate the substore commit hash against trusted appHash substoreCommitHash, err := store.VerifyMultiStoreCommitInfo(multiStoreProof.StoreName, multiStoreProof.CommitIDList, commit.Header.AppHash) if err != nil { - return nil, errors.Wrap(err, "failed in verifying the proof against appHash") + return errors.Wrap(err, "failed in verifying the proof against appHash") } err = store.VerifyRangeProof(resp.Key, resp.Value, substoreCommitHash, &multiStoreProof.RangeProof) if err != nil { - return nil, errors.Wrap(err, "failed in the range proof verification") + return errors.Wrap(err, "failed in the range proof verification") + } + return nil +} + +// query performs a query from a Tendermint node with the provided store name +// and path. +func (ctx CLIContext) query(path string, key common.HexBytes) (res []byte, err error) { + node, err := ctx.GetNode() + if err != nil { + return res, err + } + + opts := rpcclient.ABCIQueryOptions{ + Height: ctx.Height, + Trusted: ctx.TrustNode, + } + + result, err := node.ABCIQueryWithOptions(path, key, opts) + if err != nil { + return res, err + } + + resp := result.Response + if resp.Code != uint32(0) { + return res, errors.Errorf("query failed: (%d) %s", resp.Code, resp.Log) + } + + err = ctx.proofVerify(path, resp) + if err != nil { + return nil, err } return resp.Value, nil diff --git a/client/keys/add.go b/client/keys/add.go index a0ba8cce86a0..f211150a0127 100644 --- a/client/keys/add.go +++ b/client/keys/add.go @@ -187,14 +187,10 @@ func AddNewKeyRequestHandler(w http.ResponseWriter, r *http.Request) { 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 == "" { + + if paramCheck(m) != nil { w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("You have to specify a password for the locally stored account.")) + w.Write([]byte(err.Error())) return } @@ -239,8 +235,23 @@ func AddNewKeyRequestHandler(w http.ResponseWriter, r *http.Request) { w.Write(bz) } +// paramCheck performs add new key parameters checking +func paramCheck(m NewKeyBody) error { + if len(m.Name) < 1 || len(m.Name) > 16 { + return fmt.Errorf("account name length should not be longer than 16") + } + for _, char := range []rune(m.Name) { + if !syntax.IsWordChar(char) { + return fmt.Errorf("account name should not contains any char beyond [_0-9A-Za-z]") + } + } + if len(m.Password) < 8 || len(m.Password) > 16 { + return fmt.Errorf("account password length should be no less than 8 and no greater than 16") + } + return nil +} + // AddNewKeyRequest is the handler of adding new key in swagger rest server -// nolint: gocyclo func AddNewKeyRequest(gtx *gin.Context) { var m NewKeyBody body, err := ioutil.ReadAll(gtx.Request.Body) @@ -254,19 +265,8 @@ func AddNewKeyRequest(gtx *gin.Context) { return } - if len(m.Name) < 1 || len(m.Name) > 16 { - httputils.NewError(gtx, http.StatusBadRequest, fmt.Errorf("account name length should not be longer than 16")) - return - } - for _, char := range []rune(m.Name) { - if !syntax.IsWordChar(char) { - httputils.NewError(gtx, http.StatusBadRequest, fmt.Errorf("account name should not contains any char beyond [_0-9A-Za-z]")) - return - } - } - if len(m.Password) < 8 || len(m.Password) > 16 { - httputils.NewError(gtx, http.StatusBadRequest, fmt.Errorf("account password length should be no less than 8 and no greater than 16")) - return + if paramCheck(m) != nil { + httputils.NewError(gtx, http.StatusBadRequest, err) } kb, err := GetKeyBase() diff --git a/client/keys/list.go b/client/keys/list.go index 7cb4b2f65534..e54d95d70f01 100644 --- a/client/keys/list.go +++ b/client/keys/list.go @@ -69,7 +69,7 @@ func QueryKeysRequestHandler(w http.ResponseWriter, r *http.Request) { w.Write(output) } -// DeleteKeyRequest is the handler of listing all keys in swagger rest server +// QueryKeysRequest is the handler of listing all keys in swagger rest server func QueryKeysRequest(gtx *gin.Context) { kb, err := GetKeyBase() if err != nil { diff --git a/client/lcd/docs/docs.go b/client/lcd/docs/docs.go index 0af26374a551..8e7876ed1d7a 100644 --- a/client/lcd/docs/docs.go +++ b/client/lcd/docs/docs.go @@ -533,7 +533,7 @@ var doc = `{ } } }, - "/accounts/{address}": { + "/bank/balance/{address}": { "get": { "description": "Get the detailed information for specific address", "consumes": [ @@ -586,120 +586,7 @@ var doc = `{ } } }, - "/create_transfer": { - "post": { - "description": "Build transaction", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Token Operation" - ], - "summary": "Build transaction", - "parameters": [ - { - "description": "create transaction parameters", - "name": "transferBody", - "in": "body", - "required": true, - "schema": { - "type": "object", - "$ref": "#/definitions/bank.transferBody" - } - } - ], - "responses": { - "200": { - "description": "OK. The returned string is base64 encoding", - "schema": { - "type": "string" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "$ref": "#/definitions/httputil.HTTPError" - } - }, - "404": { - "description": "Not Found", - "schema": { - "type": "object", - "$ref": "#/definitions/httputil.HTTPError" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "$ref": "#/definitions/httputil.HTTPError" - } - } - } - } - }, - "/signed_transfer": { - "post": { - "description": "Broadcast signed transaction.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Token Operation" - ], - "summary": "Broadcast signed transaction", - "parameters": [ - { - "description": "Signed transaction. Transaction data, signatures and public keys should be base64 encoding", - "name": "signedTransaction", - "in": "body", - "required": true, - "schema": { - "type": "object", - "$ref": "#/definitions/bank.signedBody" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "$ref": "#/definitions/bank.ResultBroadcastTxCommit" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "$ref": "#/definitions/httputil.HTTPError" - } - }, - "404": { - "description": "Not Found", - "schema": { - "type": "object", - "$ref": "#/definitions/httputil.HTTPError" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "$ref": "#/definitions/httputil.HTTPError" - } - } - } - } - }, - "/accounts/{address}/send": { + "/bank/transfers": { "post": { "description": "This API require the Cosmos-LCD have keystore module. It will ask keystore module for transaction signature", "consumes": [ @@ -713,12 +600,6 @@ var doc = `{ ], "summary": "Send coins to a address", "parameters": [ - { - "type": "string", - "description": "address to send asset", - "name": "address", - "in": "path" - }, { "description": "transfer asset", "name": "sendAsset", @@ -726,7 +607,7 @@ var doc = `{ "required": true, "schema": { "type": "object", - "$ref": "#/definitions/bank.sendBody" + "$ref": "#/definitions/bank.transferBody" } } ], @@ -1293,45 +1174,15 @@ var doc = `{ "bank.transferBody": { "type": "object", "properties": { - "account_number": { - "type": "integer" - }, - "amount": { - "type": "integer" - }, - "denomination": { + "name": { "type": "string" }, - "ensure_account_sequence": { - "type": "boolean" - }, - "chain_id": { + "to_address": { "type": "string" }, "from_address": { "type": "string" }, - "gas": { - "type": "integer" - }, - "fee": { - "type": "string", - "example": "10 monikerToken" - }, - "sequence": { - "type": "integer" - }, - "to_address": { - "type": "string" - } - } - }, - "bank.sendBody": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, "amount": { "type": "object", "$ref": "#/definitions/sdk.Coins" @@ -1343,17 +1194,25 @@ var doc = `{ "type": "string" }, "account_number": { - "type": "integer" + "type": "string" }, "gas": { - "type": "integer" + "type": "string" }, "fee": { "type": "string", - "example": "10 monikerToken" + "example": "1steak" }, "sequence": { - "type": "integer" + "type": "string" + }, + "signed": { + "type": "boolean", + "example": true + }, + "ensure_account_sequence": { + "type": "boolean", + "example": false } } }, diff --git a/client/lcd/root.go b/client/lcd/root.go index da0e14c8dac6..4a5cfb5bec29 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -110,7 +110,7 @@ func createHandler(cdc *wire.Codec) http.Handler { func ServeSwaggerCommand(cdc *wire.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "rest-server-swagger", - Short: "Start LCD (light-client daemon), a local REST server with swagger-ui, default uri: http://localhost:1317/swagger/index.html", + Short: "Start Gaia-lite (gaia light client daemon), a local REST server with swagger-ui, default url: http://localhost:1317/swagger/index.html", RunE: func(cmd *cobra.Command, args []string) error { logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)). With("module", "rest-server-swagger") @@ -134,7 +134,7 @@ func ServeSwaggerCommand(cdc *wire.Codec) *cobra.Command { cmd.Flags().String(client.FlagListenAddr, "localhost:1317", "Address for server to listen on.") cmd.Flags().String(client.FlagNodeList, "tcp://localhost:26657", "Node list to connect to, example: \"tcp://10.10.10.10:26657,tcp://20.20.20.20:26657\".") cmd.Flags().String(client.FlagChainID, "", "ID of chain we connect to, must be specified.") - cmd.Flags().String(client.FlagSwaggerHostIP, "localhost", "The host IP of the Cosmos-LCD server, swagger will send request to this host.") + cmd.Flags().String(client.FlagSwaggerHostIP, "localhost", "The host IP of the Gaia-lite server, swagger-ui will send request to this host.") cmd.Flags().String(client.FlagModules, "general,key,token", "Enabled modules.") cmd.Flags().Bool(client.FlagTrustNode, false, "Trust full nodes or not.") @@ -154,7 +154,7 @@ func createSwaggerHandler(server *gin.Engine, cdc *wire.Codec) { //Split the node list string into multi full node URIs nodeAddrArray := strings.Split(nodeAddrs,",") if len(nodeAddrArray) < 1 { - panic(fmt.Errorf("missing node URIs")) + panic(fmt.Errorf("missing node URLs")) } //Tendermint certifier can only connect to one full node. Here we assign the first full node to it certifier, err := tendermintLiteProxy.GetCertifier(chainID, rootDir, nodeAddrArray[0]) diff --git a/x/bank/client/rest/sendtx.go b/x/bank/client/rest/sendtx.go index cfc286f4cf77..0ca01d34de35 100644 --- a/x/bank/client/rest/sendtx.go +++ b/x/bank/client/rest/sendtx.go @@ -15,10 +15,10 @@ import ( "github.com/gorilla/mux" "github.com/gin-gonic/gin" "github.com/cosmos/cosmos-sdk/x/auth" - "encoding/base64" "github.com/cosmos/cosmos-sdk/client/httputils" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" - "errors" + "bytes" + "fmt" ) // RegisterRoutes - Central function to define routes that get registered by the main application @@ -27,9 +27,11 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Codec, k } type sendBody struct { - Name string `json:"name"` - Password string `json:"password"` + // fees is not used currently + // Fees sdk.Coin `json="fees"` Amount sdk.Coins `json:"amount"` + LocalAccountName string `json:"name"` + Password string `json:"password"` ChainID string `json:"chain_id"` AccountNumber int64 `json:"account_number"` Sequence int64 `json:"sequence"` @@ -38,22 +40,18 @@ type sendBody struct { } type transferBody struct { - ChainID string `json:"chain_id"` - FromAddress string `json:"from_address"` - ToAddress string `json:"to_address"` - Amount sdk.Int `json:"amount"` - Denomination string `json:"denomination"` - AccountNumber int64 `json:"account_number"` - Sequence int64 `json:"sequence"` - EnsureAccAndSeq bool `json:"ensure_account_sequence"` - Gas int64 `json:"gas"` - Fee string `json:"fee"` -} - -type signedBody struct { - TransferBody transferBody `json:"transfer_body"` - Signature []byte `json:"signature_list"` - PublicKey []byte `json:"public_key_list"` + Name string `json:"name"` + Password string `json:"password"` + FromAddress string `json:"from_address"` + ToAddress string `json:"to_address"` + Amount sdk.Coins `json:"amount"` + ChainID string `json:"chain_id"` + AccountNumber int64 `json:"account_number"` + Sequence int64 `json:"sequence"` + Gas int64 `json:"gas"` + Fee string `json:"fee"` + Signed bool `json:"signed"` // true by default + EnsureAccAndSeq bool `json:"ensure_account_sequence"` } var msgCdc = wire.NewCodec() @@ -141,73 +139,11 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLICo // RegisterSwaggerRoutes - Central function to define routes that get registered by the main application func RegisterSwaggerRoutes(routerGroup *gin.RouterGroup, ctx context.CLIContext, cdc *wire.Codec, kb keys.Keybase) { - routerGroup.POST("/accounts/:address/send", sendRequestFn(cdc, ctx, kb)) - routerGroup.POST("/create_transfer", createTransferTxForSignFn(cdc, ctx)) - routerGroup.POST("/signed_transfer", composeAndBroadcastSignedTransferTxFn(cdc, ctx)) - routerGroup.POST("/bank/transfers", composeAndBroadcastSignedTransferTxFn(cdc, ctx)) -} - -func composeTx(cdc *wire.Codec, ctx context.CLIContext, transferBody transferBody) (auth.StdSignMsg, authctx.TxContext, sdk.Error) { - - emptyMsg := auth.StdSignMsg{} - emptyTxContext := authctx.TxContext{} - - amount := sdk.NewCoin(transferBody.Denomination, transferBody.Amount) - var amounts sdk.Coins - amounts = append(amounts, amount) - - fromAddress, err := sdk.AccAddressFromBech32(transferBody.FromAddress) - if err != nil { - return emptyMsg, emptyTxContext, sdk.ErrInvalidAddress(err.Error()) - } - - toAddress, err := sdk.AccAddressFromBech32(transferBody.ToAddress) - if err != nil { - return emptyMsg, emptyTxContext, sdk.ErrInvalidAddress(err.Error()) - } - - // build message - msg := client.BuildMsg(fromAddress, toAddress, amounts) - - accountNumber := transferBody.AccountNumber - sequence := transferBody.Sequence - gas := transferBody.Gas - fee := transferBody.Fee - - if transferBody.EnsureAccAndSeq { - if ctx.AccDecoder == nil { - ctx = ctx.WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) - } - accountNumber, err = ctx.GetAccountNumber(fromAddress) - if err != nil { - return emptyMsg, emptyTxContext, sdk.ErrInternal(err.Error()) - } - - sequence, err = ctx.GetAccountSequence(fromAddress) - if err != nil { - return emptyMsg, emptyTxContext, sdk.ErrInternal(err.Error()) - } - } - - txCtx := authctx.TxContext{ - Codec: cdc, - Gas: gas, - Fee: fee, - ChainID: transferBody.ChainID, - AccountNumber: accountNumber, - Sequence: sequence, - } - - txForSign, err := txCtx.Build([]sdk.Msg{msg}) - if err != nil { - return emptyMsg, emptyTxContext, sdk.ErrInternal(err.Error()) - } - - return txForSign, txCtx, nil + routerGroup.POST("/bank/transfers", transferRequestFn(cdc, ctx, kb)) } // handler of creating transfer transaction -func createTransferTxForSignFn(cdc *wire.Codec, ctx context.CLIContext) gin.HandlerFunc { +func transferRequestFn(cdc *wire.Codec, ctx context.CLIContext, kb keys.Keybase) gin.HandlerFunc { return func(gtx *gin.Context) { var transferBody transferBody body, err := ioutil.ReadAll(gtx.Request.Body) @@ -231,148 +167,126 @@ func createTransferTxForSignFn(cdc *wire.Codec, ctx context.CLIContext) gin.Hand return } - base64TxData := make([]byte, base64.StdEncoding.EncodedLen(len(txForSign.Bytes()))) - base64.StdEncoding.Encode(base64TxData,txForSign.Bytes()) + if !transferBody.Signed { + httputils.NormalResponse(gtx, txForSign.Bytes()) + return + } - httputils.NormalResponse(gtx,base64TxData) + signAndBroadcase(gtx, cdc, ctx, txForSign, transferBody, kb) } } -// nolint: gocyclo -// handler of composing and broadcasting transactions in swagger rest server -func composeAndBroadcastSignedTransferTxFn(cdc *wire.Codec, ctx context.CLIContext) gin.HandlerFunc { - return func(gtx *gin.Context) { - var signedTransaction signedBody - body, err := ioutil.ReadAll(gtx.Request.Body) - if err != nil { - httputils.NewError(gtx, http.StatusBadRequest, err) - return - } - err = cdc.UnmarshalJSON(body, &signedTransaction) - if err != nil { - httputils.NewError(gtx, http.StatusBadRequest, err) - return - } +// signAndBroadcase perform transaction sign and broadcast operation +func signAndBroadcase(gtx *gin.Context, cdc *wire.Codec, ctx context.CLIContext, txForSign auth.StdSignMsg, transferBody transferBody, kb keys.Keybase) { - if signedTransaction.Signature == nil || signedTransaction.PublicKey == nil { - httputils.NewError(gtx, http.StatusBadRequest, errors.New("signature or public key is empty")) - return - } + if transferBody.Name == "" || transferBody.Password == "" { + httputils.NewError(gtx, http.StatusBadRequest, fmt.Errorf("missing key name or password in signning transaction")) + return + } - signature, err := base64.StdEncoding.DecodeString(string(signedTransaction.Signature)) - if err != nil { - httputils.NewError(gtx, http.StatusBadRequest, err) - return - } - publicKey, err := base64.StdEncoding.DecodeString(string(signedTransaction.PublicKey)) - if err != nil { - httputils.NewError(gtx, http.StatusBadRequest, err) - return - } + info, err := kb.Get(transferBody.Name) + if err != nil { + httputils.NewError(gtx, http.StatusBadRequest, err) + return + } - txForSign, txCtx, errMsg := composeTx(cdc, ctx, signedTransaction.TransferBody) - if errMsg != nil { - if errMsg.Code() == sdk.CodeInternal { - httputils.NewError(gtx, http.StatusInternalServerError, errMsg) - } else { - httputils.NewError(gtx, http.StatusBadRequest, errMsg) - } - } + fromAddress, err := sdk.AccAddressFromBech32(transferBody.FromAddress) + if err != nil { + httputils.NewError(gtx, http.StatusBadRequest, err) + return + } - txDataForBroadcast, err := txCtx.BuildTxWithSignature(cdc, txForSign, signature, publicKey) - if err != nil { - httputils.NewError(gtx, http.StatusInternalServerError, err) - return - } + if !bytes.Equal(info.GetPubKey().Address(), fromAddress) { + httputils.NewError(gtx, http.StatusBadRequest, fmt.Errorf("the fromAddress doesn't equal to the address of sign key")) + return + } - res, err := ctx.BroadcastTx(txDataForBroadcast) - if err != nil { - httputils.NewError(gtx, http.StatusInternalServerError, err) - return - } + sig, pubkey, err := kb.Sign(transferBody.Name, transferBody.Password, txForSign.Bytes()) + if err != nil { + httputils.NewError(gtx, http.StatusInternalServerError, err) + return + } - output, err := wire.MarshalJSONIndent(cdc, res) - if err != nil { - httputils.NewError(gtx, http.StatusInternalServerError, err) - return - } + sigs := []auth.StdSignature{{ + AccountNumber: txForSign.AccountNumber, + Sequence: txForSign.Sequence, + PubKey: pubkey, + Signature: sig, + }} - httputils.NormalResponse(gtx, output) + txBytes, err := ctx.Codec.MarshalBinary(auth.NewStdTx(txForSign.Msgs, txForSign.Fee, sigs, txForSign.Memo)) + if err != nil { + httputils.NewError(gtx, http.StatusInternalServerError, err) + return } -} -// handler of sending tokens in swagger rest server -func sendRequestFn(cdc *wire.Codec, ctx context.CLIContext, kb keys.Keybase) gin.HandlerFunc { - return func(gtx *gin.Context) { + res, err := ctx.BroadcastTx(txBytes) + if err != nil { + httputils.NewError(gtx, http.StatusInternalServerError, err) + return + } - bech32addr := gtx.Param("address") + output, err := wire.MarshalJSONIndent(cdc, res) + if err != nil { + httputils.NewError(gtx, http.StatusInternalServerError, err) + return + } - address, err := sdk.AccAddressFromBech32(bech32addr) - if err != nil { - httputils.NewError(gtx, http.StatusBadRequest, err) - return - } + httputils.NormalResponse(gtx, output) +} - var m sendBody - body, err := ioutil.ReadAll(gtx.Request.Body) - if err != nil { - httputils.NewError(gtx, http.StatusBadRequest, err) - return - } - err = msgCdc.UnmarshalJSON(body, &m) - if err != nil { - httputils.NewError(gtx, http.StatusBadRequest, err) - return - } +// composeTx perform StdSignMsg building operation +func composeTx(cdc *wire.Codec, ctx context.CLIContext, transferBody transferBody) (auth.StdSignMsg, authctx.TxContext, sdk.Error) { - info, err := kb.Get(m.LocalAccountName) - if err != nil { - httputils.NewError(gtx, http.StatusUnauthorized, err) - return - } + emptyMsg := auth.StdSignMsg{} + emptyTxContext := authctx.TxContext{} - from := sdk.AccAddress(info.GetPubKey().Address()) + fromAddress, err := sdk.AccAddressFromBech32(transferBody.FromAddress) + if err != nil { + return emptyMsg, emptyTxContext, sdk.ErrInvalidAddress(err.Error()) + } - to, err := sdk.AccAddressFromBech32(address.String()) - if err != nil { - httputils.NewError(gtx, http.StatusBadRequest, err) - return - } + toAddress, err := sdk.AccAddressFromBech32(transferBody.ToAddress) + if err != nil { + return emptyMsg, emptyTxContext, sdk.ErrInvalidAddress(err.Error()) + } - // build message - msg := client.BuildMsg(from, to, m.Amount) - if err != nil { // XXX rechecking same error ? - httputils.NewError(gtx, http.StatusInternalServerError, err) - return - } + // build message + msg := client.BuildMsg(fromAddress, toAddress, transferBody.Amount) - txCtx := authctx.TxContext{ - Codec: cdc, - Gas: m.Gas, - Fee: m.Fee, - ChainID: m.ChainID, - AccountNumber: m.AccountNumber, - Sequence: m.Sequence, - } + accountNumber := transferBody.AccountNumber + sequence := transferBody.Sequence + gas := transferBody.Gas + fee := transferBody.Fee - txBytes, err := txCtx.BuildAndSign(m.LocalAccountName, m.Password, []sdk.Msg{msg}) - if err != nil { - httputils.NewError(gtx, http.StatusUnauthorized, err) - return + if transferBody.EnsureAccAndSeq { + if ctx.AccDecoder == nil { + ctx = ctx.WithAccountDecoder(authcmd.GetAccountDecoder(cdc)) } - - res, err := ctx.BroadcastTx(txBytes) + accountNumber, err = ctx.GetAccountNumber(fromAddress) if err != nil { - httputils.NewError(gtx, http.StatusInternalServerError, err) - return + return emptyMsg, emptyTxContext, sdk.ErrInternal(err.Error()) } - output, err := wire.MarshalJSONIndent(cdc, res) + sequence, err = ctx.GetAccountSequence(fromAddress) if err != nil { - httputils.NewError(gtx, http.StatusInternalServerError, err) - return + return emptyMsg, emptyTxContext, sdk.ErrInternal(err.Error()) } + } - httputils.NormalResponse(gtx, output) + txCtx := authctx.TxContext{ + Codec: cdc, + Gas: gas, + Fee: fee, + ChainID: transferBody.ChainID, + AccountNumber: accountNumber, + Sequence: sequence, + } + + txForSign, err := txCtx.Build([]sdk.Msg{msg}) + if err != nil { + return emptyMsg, emptyTxContext, sdk.ErrInternal(err.Error()) } + + return txForSign, txCtx, nil } \ No newline at end of file