From b6dc2dd5dd129c9d2bc71abc604aaf691270b998 Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Thu, 15 Nov 2018 13:22:19 -0800 Subject: [PATCH 1/5] Fixes #1081 --- client/flags.go | 65 +++-- client/keys/utils.go | 6 +- client/lcd/root.go | 263 ++++++++++----------- client/lcd/test_helpers.go | 24 +- cmd/gaia/cmd/gaiacli/main.go | 44 +++- docs/examples/basecoin/cmd/basecli/main.go | 28 ++- docs/examples/democoin/cmd/democli/main.go | 22 +- 7 files changed, 283 insertions(+), 169 deletions(-) diff --git a/client/flags.go b/client/flags.go index 9d03939e0240..dfadab07e43f 100644 --- a/client/flags.go +++ b/client/flags.go @@ -17,25 +17,32 @@ const ( DefaultGasLimit = 200000 GasFlagSimulate = "simulate" - FlagUseLedger = "ledger" - FlagChainID = "chain-id" - FlagNode = "node" - FlagHeight = "height" - FlagGas = "gas" - FlagGasAdjustment = "gas-adjustment" - FlagTrustNode = "trust-node" - FlagFrom = "from" - FlagName = "name" - FlagAccountNumber = "account-number" - FlagSequence = "sequence" - FlagMemo = "memo" - FlagFee = "fee" - FlagAsync = "async" - FlagJson = "json" - FlagPrintResponse = "print-response" - FlagDryRun = "dry-run" - FlagGenerateOnly = "generate-only" - FlagIndentResponse = "indent" + FlagUseLedger = "ledger" + FlagChainID = "chain-id" + FlagNode = "node" + FlagHeight = "height" + FlagGas = "gas" + FlagGasAdjustment = "gas-adjustment" + FlagTrustNode = "trust-node" + FlagFrom = "from" + FlagName = "name" + FlagAccountNumber = "account-number" + FlagSequence = "sequence" + FlagMemo = "memo" + FlagFee = "fee" + FlagAsync = "async" + FlagJson = "json" + FlagPrintResponse = "print-response" + FlagDryRun = "dry-run" + FlagGenerateOnly = "generate-only" + FlagIndentResponse = "indent" + FlagListenAddr = "laddr" + FlagCORS = "cors" + FlagMaxOpenConnections = "max-open" + FlagInsecure = "insecure" + FlagSSLHosts = "ssl-hosts" + FlagSSLCertFile = "ssl-certfile" + FlagSSLKeyFile = "ssl-keyfile" ) // LineBreak can be included in a command list to provide a blank line @@ -92,6 +99,26 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { return cmds } +// RegisterRestServerFlags registers the flags required for rest server +func RegisterRestServerFlags(cmd *cobra.Command) *cobra.Command { + cmd.Flags().String(FlagListenAddr, "tcp://localhost:1317", "The address for the server to listen on") + cmd.Flags().Bool(FlagInsecure, false, "Do not set up SSL/TLS layer") + cmd.Flags().String(FlagSSLHosts, "", "Comma-separated hostnames and IPs to generate a certificate for") + cmd.Flags().String(FlagSSLCertFile, "", "Path to a SSL certificate file. If not supplied, a self-signed certificate will be generated.") + cmd.Flags().String(FlagSSLKeyFile, "", "Path to a key file; ignored if a certificate file is not supplied.") + cmd.Flags().String(FlagCORS, "", "Set the domains that can make CORS requests (* for all)") + cmd.Flags().String(FlagChainID, "", "Chain ID of Tendermint node") + cmd.Flags().String(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(FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") + cmd.Flags().Bool(FlagIndentResponse, false, "Add indent to JSON response") + + viper.BindPFlag(FlagTrustNode, cmd.Flags().Lookup(FlagTrustNode)) + viper.BindPFlag(FlagChainID, cmd.Flags().Lookup(FlagChainID)) + viper.BindPFlag(FlagNode, cmd.Flags().Lookup(FlagNode)) + return cmd +} + // Gas flag parsing functions // GasSetting encapsulates the possible values passed through the --gas flag. diff --git a/client/keys/utils.go b/client/keys/utils.go index 742b512d3da7..f14bd5cad926 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -2,9 +2,10 @@ package keys import ( "fmt" - "github.com/syndtr/goleveldb/leveldb/opt" "path/filepath" + "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/crypto/keys" @@ -13,9 +14,10 @@ import ( "github.com/cosmos/cosmos-sdk/client" + "net/http" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "net/http" ) // KeyDBName is the directory under root where we store the keys diff --git a/client/lcd/root.go b/client/lcd/root.go index 8366b6114c52..d9404f09601a 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -10,15 +10,9 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/keys" - "github.com/cosmos/cosmos-sdk/client/rpc" - "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/codec" + keybase "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/cosmos/cosmos-sdk/server" - auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" - bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" - gov "github.com/cosmos/cosmos-sdk/x/gov/client/rest" - slashing "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" - stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest" "github.com/gorilla/mux" "github.com/rakyll/statik/fs" "github.com/spf13/cobra" @@ -27,159 +21,152 @@ import ( tmserver "github.com/tendermint/tendermint/rpc/lib/server" ) -const ( - flagListenAddr = "laddr" - flagCORS = "cors" - flagMaxOpenConnections = "max-open" - flagInsecure = "insecure" - flagSSLHosts = "ssl-hosts" - flagSSLCertFile = "ssl-certfile" - flagSSLKeyFile = "ssl-keyfile" -) +// RestServer represents the Light Client Rest server +type RestServer struct { + Mux *mux.Router + CliCtx context.CLIContext + KeyBase keybase.Keybase + Cdc *codec.Codec -// ServeCommand will generate a long-running rest server -// (aka Light Client Daemon) that exposes functionality similar -// to the cli, but over rest -func ServeCommand(cdc *codec.Codec) *cobra.Command { + log log.Logger + listener net.Listener + fingerprint string +} - cmd := &cobra.Command{ - Use: "rest-server", - Short: "Start LCD (light-client daemon), a local REST server", - RunE: func(cmd *cobra.Command, args []string) (err error) { - listenAddr := viper.GetString(flagListenAddr) - handler := createHandler(cdc) - - registerSwaggerUI(handler) - - logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server") - maxOpen := viper.GetInt(flagMaxOpenConnections) - sslHosts := viper.GetString(flagSSLHosts) - certFile := viper.GetString(flagSSLCertFile) - keyFile := viper.GetString(flagSSLKeyFile) - - var listener net.Listener - var fingerprint string - - server.TrapSignal(func() { - err := listener.Close() - logger.Error("error closing listener", "err", err) - }) - - var cleanupFunc func() - - // TODO: re-enable insecure mode once #2715 has been addressed - if viper.GetBool(flagInsecure) { - fmt.Println( - "Insecure mode is temporarily disabled, please locally generate an " + - "SSL certificate to test. Support will be re-enabled soon!", - ) - // listener, err = tmserver.StartHTTPServer( - // listenAddr, handler, logger, - // tmserver.Config{MaxOpenConnections: maxOpen}, - // ) - // if err != nil { - // return - // } - } else { - if certFile != "" { - // validateCertKeyFiles() is needed to work around tendermint/tendermint#2460 - err = validateCertKeyFiles(certFile, keyFile) - if err != nil { - return err - } - - // cert/key pair is provided, read the fingerprint - fingerprint, err = fingerprintFromFile(certFile) - if err != nil { - return err - } - } else { - // if certificate is not supplied, generate a self-signed one - certFile, keyFile, fingerprint, err = genCertKeyFilesAndReturnFingerprint(sslHosts) - if err != nil { - return err - } - - cleanupFunc = func() { - os.Remove(certFile) - os.Remove(keyFile) - } - - defer cleanupFunc() - } - - listener, err = tmserver.StartHTTPAndTLSServer( - listenAddr, handler, - certFile, keyFile, - logger, - tmserver.Config{MaxOpenConnections: maxOpen}, - ) - if err != nil { - return - } - - logger.Info(fingerprint) - logger.Info("REST server started") - } +// NewRestServer creates a new rest server instance +func NewRestServer(cdc *codec.Codec) RestServer { + r := mux.NewRouter() + cliCtx := context.NewCLIContext().WithCodec(cdc) - // logger.Info("REST server started") + // Register version methods on the router + r.HandleFunc("/version", CLIVersionRequestHandler).Methods("GET") + r.HandleFunc("/node_version", NodeVersionRequestHandler(cliCtx)).Methods("GET") - return nil - }, - } + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server") - cmd.Flags().String(flagListenAddr, "tcp://localhost:1317", "The address for the server to listen on") - cmd.Flags().Bool(flagInsecure, false, "Do not set up SSL/TLS layer") - cmd.Flags().String(flagSSLHosts, "", "Comma-separated hostnames and IPs to generate a certificate for") - cmd.Flags().String(flagSSLCertFile, "", "Path to a SSL certificate file. If not supplied, a self-signed certificate will be generated.") - cmd.Flags().String(flagSSLKeyFile, "", "Path to a key file; ignored if a certificate file is not supplied.") - cmd.Flags().String(flagCORS, "", "Set the domains that can make CORS requests (* for all)") - 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, "Trust connected full node (don't verify proofs for responses)") - cmd.Flags().Bool(client.FlagIndentResponse, false, "Add indent to JSON response") - - viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode)) - viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID)) - viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) + return RestServer{ + Mux: r, + CliCtx: cliCtx, + Cdc: cdc, - return cmd + log: logger, + } } -func createHandler(cdc *codec.Codec) *mux.Router { - r := mux.NewRouter() - +func (rs RestServer) setKeybase() { kb, err := keys.GetKeyBase() //XXX if err != nil { - panic(err) + fmt.Printf("Failed to open Keybase: %s, exiting...", err) + os.Exit(1) } + rs.KeyBase = kb +} - cliCtx := context.NewCLIContext().WithCodec(cdc) +// Start starts the rest server +func (rs RestServer) Start(listenAddr string, sslHosts string, certFile string, keyFile string, maxOpen int, insecure bool) (err error) { + + server.TrapSignal(func() { + err := rs.listener.Close() + rs.log.Error("error closing listener", "err", err) + }) + + var cleanupFunc func() + + // TODO: re-enable insecure mode once #2715 has been addressed + if insecure { + fmt.Println( + "Insecure mode is temporarily disabled, please locally generate an " + + "SSL certificate to test. Support will be re-enabled soon!", + ) + // listener, err = tmserver.StartHTTPServer( + // listenAddr, handler, logger, + // tmserver.Config{MaxOpenConnections: maxOpen}, + // ) + // if err != nil { + // return + // } + } else { + if certFile != "" { + // validateCertKeyFiles() is needed to work around tendermint/tendermint#2460 + err = validateCertKeyFiles(certFile, keyFile) + if err != nil { + return err + } - // TODO: make more functional? aka r = keys.RegisterRoutes(r) - r.HandleFunc("/version", CLIVersionRequestHandler).Methods("GET") - r.HandleFunc("/node_version", NodeVersionRequestHandler(cliCtx)).Methods("GET") + // cert/key pair is provided, read the fingerprint + rs.fingerprint, err = fingerprintFromFile(certFile) + if err != nil { + return err + } + } else { + // if certificate is not supplied, generate a self-signed one + certFile, keyFile, rs.fingerprint, err = genCertKeyFilesAndReturnFingerprint(sslHosts) + if err != nil { + return err + } + + cleanupFunc = func() { + os.Remove(certFile) + os.Remove(keyFile) + } + + defer cleanupFunc() + } + + rs.listener, err = tmserver.StartHTTPAndTLSServer( + listenAddr, rs.Mux, + certFile, keyFile, + rs.log, + tmserver.Config{MaxOpenConnections: maxOpen}, + ) + if err != nil { + return + } + + rs.log.Info(rs.fingerprint) + rs.log.Info("REST server started") + } + + // logger.Info("REST server started") + + return nil +} - keys.RegisterRoutes(r, cliCtx.Indent) - rpc.RegisterRoutes(cliCtx, r) - tx.RegisterRoutes(cliCtx, r, cdc) - auth.RegisterRoutes(cliCtx, r, cdc, "acc") - bank.RegisterRoutes(cliCtx, r, cdc, kb) - stake.RegisterRoutes(cliCtx, r, cdc, kb) - slashing.RegisterRoutes(cliCtx, r, cdc, kb) - gov.RegisterRoutes(cliCtx, r, cdc) +// ServeCommand will generate a long-running rest server +// (aka Light Client Daemon) that exposes functionality similar +// to the cli, but over rest +func (rs RestServer) ServeCommand() *cobra.Command { + + cmd := &cobra.Command{ + Use: "rest-server", + Short: "Start LCD (light-client daemon), a local REST server", + RunE: func(cmd *cobra.Command, args []string) (err error) { + rs.setKeybase() + // Start the rest server and return error if one exists + err = rs.Start( + viper.GetString(client.FlagListenAddr), + viper.GetString(client.FlagSSLHosts), + viper.GetString(client.FlagSSLCertFile), + viper.GetString(client.FlagSSLKeyFile), + viper.GetInt(client.FlagMaxOpenConnections), + viper.GetBool(client.FlagInsecure)) + + return err + }, + } + + client.RegisterRestServerFlags(cmd) - return r + return cmd } -func registerSwaggerUI(r *mux.Router) { +func (rs RestServer) registerSwaggerUI() { statikFS, err := fs.New() if err != nil { panic(err) } staticServer := http.FileServer(statikFS) - r.PathPrefix("/swagger-ui/").Handler(http.StripPrefix("/swagger-ui/", staticServer)) + rs.Mux.PathPrefix("/swagger-ui/").Handler(http.StripPrefix("/swagger-ui/", staticServer)) } func validateCertKeyFiles(certFile, keyFile string) error { diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 774fd4d6adb0..3a34073dcf53 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -19,6 +19,8 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/client/rpc" + "github.com/cosmos/cosmos-sdk/client/tx" gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/codec" crkeys "github.com/cosmos/cosmos-sdk/crypto/keys" @@ -45,6 +47,12 @@ import ( "github.com/tendermint/tendermint/proxy" tmrpc "github.com/tendermint/tendermint/rpc/lib/server" tmtypes "github.com/tendermint/tendermint/types" + + authRest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" + bankRest "github.com/cosmos/cosmos-sdk/x/bank/client/rest" + govRest "github.com/cosmos/cosmos-sdk/x/gov/client/rest" + slashingRest "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" + stakeRest "github.com/cosmos/cosmos-sdk/x/stake/client/rest" ) // makePathname creates a unique pathname for each test. It will panic if it @@ -348,7 +356,21 @@ func startTM( // // NOTE: This causes the thread to block. func startLCD(logger log.Logger, listenAddr string, cdc *codec.Codec) (net.Listener, error) { - return tmrpc.StartHTTPServer(listenAddr, createHandler(cdc), logger, tmrpc.Config{}) + rs := NewRestServer(cdc) + registerRoutes(rs) + return tmrpc.StartHTTPServer(listenAddr, rs.Mux, logger, tmrpc.Config{}) +} + +// NOTE: If making updates here also update cmd/gaia/cmd/gaiacli/main.go +func registerRoutes(rs RestServer) { + keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent) + rpc.RegisterRoutes(rs.CliCtx, rs.Mux) + tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) + authRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, "acc") + bankRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + stakeRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + slashingRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + govRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) } // Request makes a test LCD test request. It returns a response object and a diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index de99b0fc8fac..ec26bb356551 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -1,9 +1,11 @@ package main import ( + "net/http" "os" "path" + "github.com/rakyll/statik/fs" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -13,9 +15,15 @@ import ( "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/lcd" "github.com/cosmos/cosmos-sdk/client/rpc" + "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" + auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" + bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" + gov "github.com/cosmos/cosmos-sdk/x/gov/client/rest" + slashing "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" + stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest" _ "github.com/cosmos/cosmos-sdk/client/lcd/statik" ) @@ -37,15 +45,25 @@ var ( ) func main() { + // Configure cobra to sort commands cobra.EnableCommandSorting = false + + // Instantiate the codec for the command line application cdc := app.MakeCodec() + // Read in the configuration file for the sdk config := sdk.GetConfig() config.SetBech32PrefixForAccount(sdk.Bech32PrefixAccAddr, sdk.Bech32PrefixAccPub) config.SetBech32PrefixForValidator(sdk.Bech32PrefixValAddr, sdk.Bech32PrefixValPub) config.SetBech32PrefixForConsensusNode(sdk.Bech32PrefixConsAddr, sdk.Bech32PrefixConsPub) config.Seal() + // Create a new RestServer instance to serve the light client routes + rs := lcd.NewRestServer(cdc) + + // registerRoutes registers the routes on the rest server + registerRoutes(rs) + // TODO: setup keybase, viper object, etc. to be passed into // the below functions and eliminate global vars, like we do // with the cdc @@ -58,7 +76,7 @@ func main() { queryCmd(cdc), txCmd(cdc), client.LineBreak, - lcd.ServeCommand(cdc), + rs.ServeCommand(), client.LineBreak, keys.Commands(), client.LineBreak, @@ -79,6 +97,30 @@ func main() { } } +// registerRoutes registers the routes from the different modules for the LCD. +// NOTE: details on the routes added for each module are in the module documentation +// NOTE: If making updates here you also need to update the test helper in client/lcd/test_helper.go +func registerRoutes(rs lcd.RestServer) { + registerSwaggerUI(rs) + keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent) + rpc.RegisterRoutes(rs.CliCtx, rs.Mux) + tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) + auth.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, "acc") + bank.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + stake.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + slashing.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + gov.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) +} + +func registerSwaggerUI(rs lcd.RestServer) { + statikFS, err := fs.New() + if err != nil { + panic(err) + } + staticServer := http.FileServer(statikFS) + rs.Mux.PathPrefix("/swagger-ui/").Handler(http.StripPrefix("/swagger-ui/", staticServer)) +} + func initConfig(cmd *cobra.Command) error { home, err := cmd.PersistentFlags().GetString(cli.HomeFlag) if err != nil { diff --git a/docs/examples/basecoin/cmd/basecli/main.go b/docs/examples/basecoin/cmd/basecli/main.go index 3a16c8a97bbd..a0cb26dd11f4 100644 --- a/docs/examples/basecoin/cmd/basecli/main.go +++ b/docs/examples/basecoin/cmd/basecli/main.go @@ -9,12 +9,17 @@ import ( "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/docs/examples/basecoin/app" "github.com/cosmos/cosmos-sdk/docs/examples/basecoin/types" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" + bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/client/cli" slashingcmd "github.com/cosmos/cosmos-sdk/x/slashing/client/cli" + slashing "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli" + stake "github.com/cosmos/cosmos-sdk/x/stake/client/rest" "github.com/spf13/cobra" "github.com/tendermint/tendermint/libs/cli" ) @@ -34,6 +39,17 @@ func main() { // get the codec cdc := app.MakeCodec() + // Setup certain SDK config + config := sdk.GetConfig() + config.SetBech32PrefixForAccount("baseacc", "basepub") + config.SetBech32PrefixForValidator("baseval", "basevalpub") + config.SetBech32PrefixForConsensusNode("basecons", "baseconspub") + config.Seal() + + rs := lcd.NewRestServer(cdc) + + registerRoutes(rs) + // TODO: Setup keybase, viper object, etc. to be passed into // the below functions and eliminate global vars, like we do // with the cdc. @@ -83,7 +99,7 @@ func main() { // add proxy, version and key info rootCmd.AddCommand( client.LineBreak, - lcd.ServeCommand(cdc), + rs.ServeCommand(), keys.Commands(), client.LineBreak, version.VersionCmd, @@ -97,3 +113,13 @@ func main() { panic(err) } } + +func registerRoutes(rs lcd.RestServer) { + keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent) + rpc.RegisterRoutes(rs.CliCtx, rs.Mux) + tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) + auth.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, "acc") + bank.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + stake.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + slashing.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) +} diff --git a/docs/examples/democoin/cmd/democli/main.go b/docs/examples/democoin/cmd/democli/main.go index adb6169c9ee5..0dcb45fa0ca5 100644 --- a/docs/examples/democoin/cmd/democli/main.go +++ b/docs/examples/democoin/cmd/democli/main.go @@ -13,8 +13,9 @@ import ( "github.com/cosmos/cosmos-sdk/version" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" + auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" - ibccmd "github.com/cosmos/cosmos-sdk/x/ibc/client/cli" + bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" "github.com/cosmos/cosmos-sdk/docs/examples/democoin/app" "github.com/cosmos/cosmos-sdk/docs/examples/democoin/types" @@ -47,6 +48,10 @@ func main() { config.SetBech32PrefixForConsensusNode("democons", "democonspub") config.Seal() + rs := lcd.NewRestServer(cdc) + + registerRoutes(rs) + // TODO: setup keybase, viper object, etc. to be passed into // the below functions and eliminate global vars, like we do // with the cdc @@ -74,11 +79,6 @@ func main() { )...) rootCmd.AddCommand( client.PostCommands( - ibccmd.IBCTransferCmd(cdc), - )...) - rootCmd.AddCommand( - client.PostCommands( - ibccmd.IBCRelayCmd(cdc), simplestakingcmd.BondTxCmd(cdc), )...) rootCmd.AddCommand( @@ -96,7 +96,7 @@ func main() { // add proxy, version and key info rootCmd.AddCommand( client.LineBreak, - lcd.ServeCommand(cdc), + rs.ServeCommand(), keys.Commands(), client.LineBreak, version.VersionCmd, @@ -110,3 +110,11 @@ func main() { panic(err) } } + +func registerRoutes(rs lcd.RestServer) { + keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent) + rpc.RegisterRoutes(rs.CliCtx, rs.Mux) + tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) + auth.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, "acc") + bank.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) +} From 17ab9b528dc0a58d44044ac411857759eb3cf263 Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Thu, 15 Nov 2018 16:38:42 -0800 Subject: [PATCH 2/5] Add PENDING.md --- PENDING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/PENDING.md b/PENDING.md index 357d1c8b0b79..189928899762 100644 --- a/PENDING.md +++ b/PENDING.md @@ -45,6 +45,7 @@ FEATURES IMPROVEMENTS * Gaia REST API (`gaiacli advanced rest-server`) + * [\#2836](https://github.com/cosmos/cosmos-sdk/pull/2836) Expose LCD router to allow users to register routes there. * Gaia CLI (`gaiacli`) * [\#2749](https://github.com/cosmos/cosmos-sdk/pull/2749) Add --chain-id flag to gaiad testnet From 46e799429dbc086c8fd0a149ec32e2f2421e16d4 Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Fri, 16 Nov 2018 08:27:08 -0800 Subject: [PATCH 3/5] Address PR comments --- client/keys/utils.go | 17 ++++++----------- client/lcd/root.go | 12 ++++-------- cmd/gaia/cmd/gaiacli/main.go | 2 +- 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/client/keys/utils.go b/client/keys/utils.go index f14bd5cad926..0ac4a9c90727 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -2,22 +2,17 @@ package keys import ( "fmt" + "net/http" "path/filepath" - "github.com/syndtr/goleveldb/leveldb/opt" - - "github.com/spf13/viper" - - "github.com/cosmos/cosmos-sdk/crypto/keys" - "github.com/tendermint/tendermint/libs/cli" - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/cosmos/cosmos-sdk/client" - - "net/http" - "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/spf13/viper" + "github.com/syndtr/goleveldb/leveldb/opt" + "github.com/tendermint/tendermint/libs/cli" + dbm "github.com/tendermint/tendermint/libs/db" ) // KeyDBName is the directory under root where we store the keys diff --git a/client/lcd/root.go b/client/lcd/root.go index d9404f09601a..1b22a8161166 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -63,15 +63,14 @@ func (rs RestServer) setKeybase() { } // Start starts the rest server -func (rs RestServer) Start(listenAddr string, sslHosts string, certFile string, keyFile string, maxOpen int, insecure bool) (err error) { +func (rs RestServer) Start(listenAddr string, sslHosts string, + certFile string, keyFile string, maxOpen int, insecure bool) (err error) { server.TrapSignal(func() { err := rs.listener.Close() rs.log.Error("error closing listener", "err", err) }) - var cleanupFunc func() - // TODO: re-enable insecure mode once #2715 has been addressed if insecure { fmt.Println( @@ -105,12 +104,10 @@ func (rs RestServer) Start(listenAddr string, sslHosts string, certFile string, return err } - cleanupFunc = func() { + defer func() { os.Remove(certFile) os.Remove(keyFile) - } - - defer cleanupFunc() + }() } rs.listener, err = tmserver.StartHTTPAndTLSServer( @@ -136,7 +133,6 @@ func (rs RestServer) Start(listenAddr string, sslHosts string, certFile string, // (aka Light Client Daemon) that exposes functionality similar // to the cli, but over rest func (rs RestServer) ServeCommand() *cobra.Command { - cmd := &cobra.Command{ Use: "rest-server", Short: "Start LCD (light-client daemon), a local REST server", diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index ec26bb356551..af132f05959f 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -100,7 +100,7 @@ func main() { // registerRoutes registers the routes from the different modules for the LCD. // NOTE: details on the routes added for each module are in the module documentation // NOTE: If making updates here you also need to update the test helper in client/lcd/test_helper.go -func registerRoutes(rs lcd.RestServer) { +func RegisterRoutes(rs lcd.RestServer) { registerSwaggerUI(rs) keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent) rpc.RegisterRoutes(rs.CliCtx, rs.Mux) From 69830d0c7798a8a67cb6055ffc855eb3c710e826 Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Fri, 16 Nov 2018 12:07:27 -0800 Subject: [PATCH 4/5] Fix build error --- cmd/gaia/cmd/gaiacli/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index af132f05959f..ec26bb356551 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -100,7 +100,7 @@ func main() { // registerRoutes registers the routes from the different modules for the LCD. // NOTE: details on the routes added for each module are in the module documentation // NOTE: If making updates here you also need to update the test helper in client/lcd/test_helper.go -func RegisterRoutes(rs lcd.RestServer) { +func registerRoutes(rs lcd.RestServer) { registerSwaggerUI(rs) keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent) rpc.RegisterRoutes(rs.CliCtx, rs.Mux) From c63b76e0b927eb514ed84fec526df9aa63b411e8 Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Fri, 16 Nov 2018 14:10:52 -0800 Subject: [PATCH 5/5] Fix missing keybase issue --- client/lcd/root.go | 21 ++++++++++++++------- client/lcd/test_helpers.go | 14 +++++++++++--- cmd/gaia/cmd/gaiacli/main.go | 4 ++-- docs/examples/basecoin/cmd/basecli/main.go | 2 +- docs/examples/democoin/cmd/democli/main.go | 2 +- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/client/lcd/root.go b/client/lcd/root.go index 1b22a8161166..3d61816467fc 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -34,7 +34,7 @@ type RestServer struct { } // NewRestServer creates a new rest server instance -func NewRestServer(cdc *codec.Codec) RestServer { +func NewRestServer(cdc *codec.Codec) *RestServer { r := mux.NewRouter() cliCtx := context.NewCLIContext().WithCodec(cdc) @@ -44,7 +44,7 @@ func NewRestServer(cdc *codec.Codec) RestServer { logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server") - return RestServer{ + return &RestServer{ Mux: r, CliCtx: cliCtx, Cdc: cdc, @@ -53,7 +53,14 @@ func NewRestServer(cdc *codec.Codec) RestServer { } } -func (rs RestServer) setKeybase() { +func (rs *RestServer) setKeybase(kb keybase.Keybase) { + // If a keybase is passed in, set it and return + if kb != nil { + rs.KeyBase = kb + return + } + + // Otherwise get the keybase and set it kb, err := keys.GetKeyBase() //XXX if err != nil { fmt.Printf("Failed to open Keybase: %s, exiting...", err) @@ -63,7 +70,7 @@ func (rs RestServer) setKeybase() { } // Start starts the rest server -func (rs RestServer) Start(listenAddr string, sslHosts string, +func (rs *RestServer) Start(listenAddr string, sslHosts string, certFile string, keyFile string, maxOpen int, insecure bool) (err error) { server.TrapSignal(func() { @@ -132,12 +139,12 @@ func (rs RestServer) Start(listenAddr string, sslHosts string, // ServeCommand will generate a long-running rest server // (aka Light Client Daemon) that exposes functionality similar // to the cli, but over rest -func (rs RestServer) ServeCommand() *cobra.Command { +func (rs *RestServer) ServeCommand() *cobra.Command { cmd := &cobra.Command{ Use: "rest-server", Short: "Start LCD (light-client daemon), a local REST server", RunE: func(cmd *cobra.Command, args []string) (err error) { - rs.setKeybase() + rs.setKeybase(nil) // Start the rest server and return error if one exists err = rs.Start( viper.GetString(client.FlagListenAddr), @@ -156,7 +163,7 @@ func (rs RestServer) ServeCommand() *cobra.Command { return cmd } -func (rs RestServer) registerSwaggerUI() { +func (rs *RestServer) registerSwaggerUI() { statikFS, err := fs.New() if err != nil { panic(err) diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 3a34073dcf53..4fbacf3d5813 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -111,6 +111,13 @@ func GetKeyBase(t *testing.T) crkeys.Keybase { return keybase } +// GetTestKeyBase fetches the current testing keybase +func GetTestKeyBase(t *testing.T) crkeys.Keybase { + keybase, err := keys.GetKeyBaseWithWritePerm() + require.NoError(t, err) + return keybase +} + // CreateAddr adds an address to the key store and returns an address and seed. // It also requires that the key could be created. func CreateAddr(t *testing.T, name, password string, kb crkeys.Keybase) (sdk.AccAddress, string) { @@ -296,7 +303,7 @@ func InitializeTestLCD( require.NoError(t, err) tests.WaitForNextHeightTM(tests.ExtractPortFromAddress(config.RPC.ListenAddress)) - lcd, err := startLCD(logger, listenAddr, cdc) + lcd, err := startLCD(logger, listenAddr, cdc, t) require.NoError(t, err) tests.WaitForLCDStart(port) @@ -355,14 +362,15 @@ func startTM( // startLCD starts the LCD. // // NOTE: This causes the thread to block. -func startLCD(logger log.Logger, listenAddr string, cdc *codec.Codec) (net.Listener, error) { +func startLCD(logger log.Logger, listenAddr string, cdc *codec.Codec, t *testing.T) (net.Listener, error) { rs := NewRestServer(cdc) + rs.setKeybase(GetTestKeyBase(t)) registerRoutes(rs) return tmrpc.StartHTTPServer(listenAddr, rs.Mux, logger, tmrpc.Config{}) } // NOTE: If making updates here also update cmd/gaia/cmd/gaiacli/main.go -func registerRoutes(rs RestServer) { +func registerRoutes(rs *RestServer) { keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent) rpc.RegisterRoutes(rs.CliCtx, rs.Mux) tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index ec26bb356551..3dd936f8635a 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -100,7 +100,7 @@ func main() { // registerRoutes registers the routes from the different modules for the LCD. // NOTE: details on the routes added for each module are in the module documentation // NOTE: If making updates here you also need to update the test helper in client/lcd/test_helper.go -func registerRoutes(rs lcd.RestServer) { +func registerRoutes(rs *lcd.RestServer) { registerSwaggerUI(rs) keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent) rpc.RegisterRoutes(rs.CliCtx, rs.Mux) @@ -112,7 +112,7 @@ func registerRoutes(rs lcd.RestServer) { gov.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) } -func registerSwaggerUI(rs lcd.RestServer) { +func registerSwaggerUI(rs *lcd.RestServer) { statikFS, err := fs.New() if err != nil { panic(err) diff --git a/docs/examples/basecoin/cmd/basecli/main.go b/docs/examples/basecoin/cmd/basecli/main.go index a0cb26dd11f4..ad32bf516978 100644 --- a/docs/examples/basecoin/cmd/basecli/main.go +++ b/docs/examples/basecoin/cmd/basecli/main.go @@ -114,7 +114,7 @@ func main() { } } -func registerRoutes(rs lcd.RestServer) { +func registerRoutes(rs *lcd.RestServer) { keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent) rpc.RegisterRoutes(rs.CliCtx, rs.Mux) tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) diff --git a/docs/examples/democoin/cmd/democli/main.go b/docs/examples/democoin/cmd/democli/main.go index 0dcb45fa0ca5..d101da2d9c38 100644 --- a/docs/examples/democoin/cmd/democli/main.go +++ b/docs/examples/democoin/cmd/democli/main.go @@ -111,7 +111,7 @@ func main() { } } -func registerRoutes(rs lcd.RestServer) { +func registerRoutes(rs *lcd.RestServer) { keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent) rpc.RegisterRoutes(rs.CliCtx, rs.Mux) tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc)