From 6a05b83069bb6a655b30de782d9cc14f6639431e Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Mon, 15 Jun 2020 13:39:09 -0400 Subject: [PATCH] Merge PR #6426: Migrate API Server --- .gitattributes | 2 +- CHANGELOG.md | 1 + Makefile | 6 +- client/context.go | 18 ++- client/{lcd => docs}/statik/init.go | 2 +- client/{lcd => docs}/statik/statik.go | 0 .../swagger-ui/favicon-16x16.png | Bin .../swagger-ui/favicon-32x32.png | Bin client/{lcd => docs}/swagger-ui/index.html | 0 .../swagger-ui/oauth2-redirect.html | 0 .../swagger-ui/swagger-ui-bundle.js | 0 .../swagger-ui-standalone-preset.js | 0 .../{lcd => docs}/swagger-ui/swagger-ui.css | 0 client/{lcd => docs}/swagger-ui/swagger.yaml | 0 client/flags/flags.go | 69 ++++------ client/lcd/root.go | 128 ------------------ docker-compose.yml | 4 + networks/local/simappnode/Dockerfile | 2 +- server/api/server.go | 77 +++++++++++ server/config/config.go | 49 ++++++- server/config/toml.go | 52 +++++-- server/constructors.go | 12 +- server/start.go | 49 ++++++- server/start_test.go | 2 +- server/util.go | 7 +- simapp/app.go | 11 ++ simapp/cmd/simcli/main.go | 11 -- simapp/cmd/simd/main.go | 2 +- simapp/cmd/simd/testnet.go | 1 + 29 files changed, 287 insertions(+), 218 deletions(-) rename client/{lcd => docs}/statik/init.go (68%) rename client/{lcd => docs}/statik/statik.go (100%) rename client/{lcd => docs}/swagger-ui/favicon-16x16.png (100%) rename client/{lcd => docs}/swagger-ui/favicon-32x32.png (100%) rename client/{lcd => docs}/swagger-ui/index.html (100%) rename client/{lcd => docs}/swagger-ui/oauth2-redirect.html (100%) rename client/{lcd => docs}/swagger-ui/swagger-ui-bundle.js (100%) rename client/{lcd => docs}/swagger-ui/swagger-ui-standalone-preset.js (100%) rename client/{lcd => docs}/swagger-ui/swagger-ui.css (100%) rename client/{lcd => docs}/swagger-ui/swagger.yaml (100%) delete mode 100644 client/lcd/root.go create mode 100644 server/api/server.go diff --git a/.gitattributes b/.gitattributes index 66cf55801..f13ccebea 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -client/lcd/swagger-ui/* linguist-vendored +client/docs/swagger-ui/* linguist-vendored diff --git a/CHANGELOG.md b/CHANGELOG.md index 34db9f4a9..1e2c3d552 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Client Breaking +* (api) [\#6426](https://github.com/cosmos/cosmos-sdk/pull/6426) The ability to start an out-of-process API REST server has now been removed. Instead, the API server is now started in-process along with the application and Tendermint. Configuration options have been added to `app.toml` to enable/disable the API server along with additional HTTP server options. * (baseapp) [\#6384](https://github.com/cosmos/cosmos-sdk/pull/6384) The `Result.Data` is now a Protocol Buffer encoded binary blob of type `TxData`. The `TxData` contains `Data` which contains a list of Protocol Buffer encoded message data and the corresponding message type. * (x/gov) [#6295](https://github.com/cosmos/cosmos-sdk/pull/6295) Fix typo in querying governance params. * (x/auth) [\#6054](https://github.com/cosmos/cosmos-sdk/pull/6054) Remove custom JSON marshaling for base accounts as multsigs cannot be bech32 decoded. diff --git a/Makefile b/Makefile index 5d31ded43..b2eb1db8b 100644 --- a/Makefile +++ b/Makefile @@ -217,9 +217,9 @@ lint: .PHONY: lint format: - find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" -not -path "./tests/mocks/*" -not -name '*.pb.go' | xargs gofmt -w -s - find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" -not -path "./tests/mocks/*" -not -name '*.pb.go' | xargs misspell -w - find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/lcd/statik/statik.go" -not -path "./tests/mocks/*" -not -name '*.pb.go' | xargs goimports -w -local github.com/cosmos/cosmos-sdk + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/docs/statik/statik.go" -not -path "./tests/mocks/*" -not -name '*.pb.go' | xargs gofmt -w -s + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/docs/statik/statik.go" -not -path "./tests/mocks/*" -not -name '*.pb.go' | xargs misspell -w + find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" -not -path "./client/docs/statik/statik.go" -not -path "./tests/mocks/*" -not -name '*.pb.go' | xargs goimports -w -local github.com/cosmos/cosmos-sdk .PHONY: format ############################################################################### diff --git a/client/context.go b/client/context.go index 88a96014b..cffa8ba76 100644 --- a/client/context.go +++ b/client/context.go @@ -21,8 +21,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// Context implements a typical context created in SDK modules for -// transaction handling and queries. +// Context implements a typical context created in SDK modules for transaction +// handling and queries. type Context struct { FromAddress sdk.AccAddress Client rpcclient.Client @@ -34,10 +34,8 @@ type Context struct { OutputFormat string Height int64 HomeDir string - NodeURI string From string BroadcastMode string - Verifier tmlite.Verifier FromName string TrustNode bool UseLedger bool @@ -49,6 +47,12 @@ type Context struct { TxGenerator TxGenerator AccountRetriever AccountRetriever + // TODO: API and CLI interfaces are migrating to a single binary (i.e be part of + // the same process of the application). We need to groom through these fields + // and remove any that no longer make sense. + NodeURI string + Verifier tmlite.Verifier + // TODO: Deprecated (remove). Codec *codec.Codec } @@ -267,6 +271,12 @@ func (ctx Context) WithChainID(chainID string) Context { return ctx } +// WithHomeDir returns a copy of the Context with HomeDir set. +func (ctx Context) WithHomeDir(dir string) Context { + ctx.HomeDir = dir + return ctx +} + // WithGenerateOnly returns a copy of the context with updated GenerateOnly value func (ctx Context) WithGenerateOnly(generateOnly bool) Context { ctx.GenerateOnly = generateOnly diff --git a/client/lcd/statik/init.go b/client/docs/statik/init.go similarity index 68% rename from client/lcd/statik/init.go rename to client/docs/statik/init.go index 9633aeb29..7d91b40fc 100644 --- a/client/lcd/statik/init.go +++ b/client/docs/statik/init.go @@ -1,3 +1,3 @@ package statik -//This just for fixing the error in importing empty github.com/cosmos/cosmos-sdk/client/lcd/statik +//This just for fixing the error in importing empty github.com/cosmos/cosmos-sdk/client/docs/statik diff --git a/client/lcd/statik/statik.go b/client/docs/statik/statik.go similarity index 100% rename from client/lcd/statik/statik.go rename to client/docs/statik/statik.go diff --git a/client/lcd/swagger-ui/favicon-16x16.png b/client/docs/swagger-ui/favicon-16x16.png similarity index 100% rename from client/lcd/swagger-ui/favicon-16x16.png rename to client/docs/swagger-ui/favicon-16x16.png diff --git a/client/lcd/swagger-ui/favicon-32x32.png b/client/docs/swagger-ui/favicon-32x32.png similarity index 100% rename from client/lcd/swagger-ui/favicon-32x32.png rename to client/docs/swagger-ui/favicon-32x32.png diff --git a/client/lcd/swagger-ui/index.html b/client/docs/swagger-ui/index.html similarity index 100% rename from client/lcd/swagger-ui/index.html rename to client/docs/swagger-ui/index.html diff --git a/client/lcd/swagger-ui/oauth2-redirect.html b/client/docs/swagger-ui/oauth2-redirect.html similarity index 100% rename from client/lcd/swagger-ui/oauth2-redirect.html rename to client/docs/swagger-ui/oauth2-redirect.html diff --git a/client/lcd/swagger-ui/swagger-ui-bundle.js b/client/docs/swagger-ui/swagger-ui-bundle.js similarity index 100% rename from client/lcd/swagger-ui/swagger-ui-bundle.js rename to client/docs/swagger-ui/swagger-ui-bundle.js diff --git a/client/lcd/swagger-ui/swagger-ui-standalone-preset.js b/client/docs/swagger-ui/swagger-ui-standalone-preset.js similarity index 100% rename from client/lcd/swagger-ui/swagger-ui-standalone-preset.js rename to client/docs/swagger-ui/swagger-ui-standalone-preset.js diff --git a/client/lcd/swagger-ui/swagger-ui.css b/client/docs/swagger-ui/swagger-ui.css similarity index 100% rename from client/lcd/swagger-ui/swagger-ui.css rename to client/docs/swagger-ui/swagger-ui.css diff --git a/client/lcd/swagger-ui/swagger.yaml b/client/docs/swagger-ui/swagger.yaml similarity index 100% rename from client/lcd/swagger-ui/swagger.yaml rename to client/docs/swagger-ui/swagger.yaml diff --git a/client/flags/flags.go b/client/flags/flags.go index fb4438da7..1f76f27be 100644 --- a/client/flags/flags.go +++ b/client/flags/flags.go @@ -39,37 +39,31 @@ const ( // List of CLI flags const ( - FlagHome = tmcli.HomeFlag - FlagUseLedger = "ledger" - FlagChainID = "chain-id" - FlagNode = "node" - FlagHeight = "height" - FlagGasAdjustment = "gas-adjustment" - FlagTrustNode = "trust-node" - FlagFrom = "from" - FlagName = "name" - FlagAccountNumber = "account-number" - FlagSequence = "sequence" - FlagMemo = "memo" - FlagFees = "fees" - FlagGasPrices = "gas-prices" - FlagBroadcastMode = "broadcast-mode" - FlagDryRun = "dry-run" - FlagGenerateOnly = "generate-only" - FlagOffline = "offline" - FlagIndentResponse = "indent" - FlagListenAddr = "laddr" - FlagMaxOpenConnections = "max-open" - FlagRPCReadTimeout = "read-timeout" - FlagRPCWriteTimeout = "write-timeout" - FlagRPCMaxBodyBytes = "max-body-bytes" - FlagOutputDocument = "output-document" // inspired by wget -O - FlagSkipConfirmation = "yes" - FlagProve = "prove" - FlagKeyringBackend = "keyring-backend" - FlagPage = "page" - FlagLimit = "limit" - FlagUnsafeCORS = "unsafe-cors" + FlagHome = tmcli.HomeFlag + FlagUseLedger = "ledger" + FlagChainID = "chain-id" + FlagNode = "node" + FlagHeight = "height" + FlagGasAdjustment = "gas-adjustment" + FlagTrustNode = "trust-node" + FlagFrom = "from" + FlagName = "name" + FlagAccountNumber = "account-number" + FlagSequence = "sequence" + FlagMemo = "memo" + FlagFees = "fees" + FlagGasPrices = "gas-prices" + FlagBroadcastMode = "broadcast-mode" + FlagDryRun = "dry-run" + FlagGenerateOnly = "generate-only" + FlagOffline = "offline" + FlagIndentResponse = "indent" + FlagOutputDocument = "output-document" // inspired by wget -O + FlagSkipConfirmation = "yes" + FlagProve = "prove" + FlagKeyringBackend = "keyring-backend" + FlagPage = "page" + FlagLimit = "limit" ) // LineBreak can be included in a command list to provide a blank line @@ -141,19 +135,6 @@ 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 = GetCommands(cmd)[0] - cmd.Flags().String(FlagListenAddr, "tcp://localhost:1317", "The address for the server to listen on") - cmd.Flags().Uint(FlagMaxOpenConnections, 1000, "The number of maximum open connections") - cmd.Flags().Uint(FlagRPCReadTimeout, 10, "The RPC read timeout (in seconds)") - cmd.Flags().Uint(FlagRPCWriteTimeout, 10, "The RPC write timeout (in seconds)") - cmd.Flags().Uint(FlagRPCMaxBodyBytes, 1000000, "The RPC max body bytes") - cmd.Flags().Bool(FlagUnsafeCORS, false, "Allows CORS requests from all domains. For development purposes only, use it at your own risk.") - - return cmd -} - // Gas flag parsing functions // GasSetting encapsulates the possible values passed through the --gas flag. diff --git a/client/lcd/root.go b/client/lcd/root.go deleted file mode 100644 index e2784f06e..000000000 --- a/client/lcd/root.go +++ /dev/null @@ -1,128 +0,0 @@ -package lcd - -import ( - "fmt" - "net" - "net/http" - "os" - "time" - - "github.com/gorilla/handlers" - "github.com/gorilla/mux" - "github.com/rakyll/statik/fs" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/tendermint/tendermint/libs/log" - tmrpcserver "github.com/tendermint/tendermint/rpc/jsonrpc/server" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/server" - - // unnamed import of statik for swagger UI support - _ "github.com/cosmos/cosmos-sdk/client/lcd/statik" -) - -// RestServer represents the Light Client Rest server -type RestServer struct { - Mux *mux.Router - ClientCtx client.Context - - log log.Logger - listener net.Listener -} - -// NewRestServer creates a new rest server instance -func NewRestServer(cdc *codec.Codec) *RestServer { - r := mux.NewRouter() - clientCtx := client.NewContext().WithCodec(cdc) - logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "rest-server") - - return &RestServer{ - Mux: r, - ClientCtx: clientCtx, - log: logger, - } -} - -// StartWithConfig starts the REST server that listens on the provided listenAddr. -// It will use the provided RPC configuration. -func (rs *RestServer) StartWithConfig(listenAddr string, cors bool, cfg *tmrpcserver.Config) error { - server.TrapSignal(func() { - err := rs.listener.Close() - rs.log.Error("error closing listener", "err", err) - }) - - listener, err := tmrpcserver.Listen(listenAddr, cfg) - if err != nil { - return err - } - - rs.listener = listener - - rs.log.Info( - fmt.Sprintf("Starting application REST service (chain-id: %q)...", viper.GetString(flags.FlagChainID)), - ) - - var h http.Handler = rs.Mux - - if cors { - return tmrpcserver.Serve(rs.listener, handlers.CORS()(h), rs.log, cfg) - } - - return tmrpcserver.Serve(rs.listener, rs.Mux, rs.log, cfg) -} - -// Start starts the REST server that listens on the provided listenAddr. The REST -// service will use Tendermint's default RPC configuration, where the R/W timeout -// and max open connections are overridden. -func (rs *RestServer) Start(listenAddr string, maxOpen int, readTimeout, writeTimeout uint, cors bool) error { - cfg := tmrpcserver.DefaultConfig() - cfg.MaxOpenConnections = maxOpen - cfg.ReadTimeout = time.Duration(readTimeout) * time.Second - cfg.WriteTimeout = time.Duration(writeTimeout) * time.Second - - return rs.StartWithConfig(listenAddr, cors, cfg) -} - -// ServeCommand will start the application REST service as a blocking process. It -// takes a codec to create a RestServer object and a function to register all -// necessary routes. -func ServeCommand(cdc *codec.Codec, registerRoutesFn func(*RestServer)) *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 := NewRestServer(cdc) - - registerRoutesFn(rs) - rs.registerSwaggerUI() - - cfg := tmrpcserver.DefaultConfig() - cfg.MaxOpenConnections = viper.GetInt(flags.FlagMaxOpenConnections) - cfg.ReadTimeout = time.Duration(viper.GetInt64(flags.FlagRPCReadTimeout)) * time.Second - cfg.WriteTimeout = time.Duration(viper.GetInt64(flags.FlagRPCWriteTimeout)) * time.Second - cfg.MaxBodyBytes = viper.GetInt64(flags.FlagRPCMaxBodyBytes) - - // start the rest server and return error if one exists - return rs.StartWithConfig( - viper.GetString(flags.FlagListenAddr), - viper.GetBool(flags.FlagUnsafeCORS), - cfg, - ) - }, - } - - return flags.RegisterRestServerFlags(cmd) -} - -func (rs *RestServer) registerSwaggerUI() { - statikFS, err := fs.New() - if err != nil { - panic(err) - } - - staticServer := http.FileServer(statikFS) - rs.Mux.PathPrefix("/").Handler(staticServer) -} diff --git a/docker-compose.yml b/docker-compose.yml index 5c2f24697..7e7ce18cb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,6 +6,7 @@ services: image: "cosmos-sdk/simappnode" ports: - "26656-26657:26656-26657" + - "1317:1317" environment: - ID=0 - LOG=${LOG:-simd.log} @@ -20,6 +21,7 @@ services: image: "cosmos-sdk/simappnode" ports: - "26659-26660:26656-26657" + - "1318:1317" environment: - ID=1 - LOG=${LOG:-simd.log} @@ -37,6 +39,7 @@ services: - LOG=${LOG:-simd.log} ports: - "26661-26662:26656-26657" + - "1319:1317" volumes: - ./build:/simd:Z networks: @@ -51,6 +54,7 @@ services: - LOG=${LOG:-simd.log} ports: - "26663-26664:26656-26657" + - "1320:1317" volumes: - ./build:/simd:Z networks: diff --git a/networks/local/simappnode/Dockerfile b/networks/local/simappnode/Dockerfile index a5a0041e7..ef70bbaf3 100644 --- a/networks/local/simappnode/Dockerfile +++ b/networks/local/simappnode/Dockerfile @@ -15,7 +15,7 @@ RUN apt-get update && \ VOLUME [ /simd ] WORKDIR /simd -EXPOSE 26656 26657 +EXPOSE 26656 26657 1317 ENTRYPOINT ["/usr/bin/wrapper.sh"] CMD ["start"] STOPSIGNAL SIGTERM diff --git a/server/api/server.go b/server/api/server.go new file mode 100644 index 000000000..c03aadc19 --- /dev/null +++ b/server/api/server.go @@ -0,0 +1,77 @@ +package api + +import ( + "net" + "net/http" + "os" + "time" + + "github.com/gorilla/handlers" + "github.com/gorilla/mux" + "github.com/rakyll/statik/fs" + "github.com/tendermint/tendermint/libs/log" + tmrpcserver "github.com/tendermint/tendermint/rpc/jsonrpc/server" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/server/config" + + // unnamed import of statik for swagger UI support + _ "github.com/cosmos/cosmos-sdk/client/docs/statik" +) + +// Server defines the server's API interface. +type Server struct { + Router *mux.Router + ClientCtx client.Context + + logger log.Logger + listener net.Listener +} + +func New(clientCtx client.Context) *Server { + return &Server{ + Router: mux.NewRouter(), + ClientCtx: clientCtx, + logger: log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "api-server"), + } +} + +// Start starts the API server. Internally, the API server leverages Tendermint's +// JSON RPC server. Configuration options are provided via config.APIConfig +// and are delegated to the Tendermint JSON RPC server. The process is +// non-blocking, so an external signal handler must be used. +func (s *Server) Start(cfg config.APIConfig) error { + if cfg.Swagger { + s.registerSwaggerUI() + } + + tmCfg := tmrpcserver.DefaultConfig() + tmCfg.MaxOpenConnections = int(cfg.MaxOpenConnections) + tmCfg.ReadTimeout = time.Duration(cfg.RPCReadTimeout) * time.Second + tmCfg.WriteTimeout = time.Duration(cfg.RPCWriteTimeout) * time.Second + tmCfg.MaxBodyBytes = int64(cfg.RPCMaxBodyBytes) + + listener, err := tmrpcserver.Listen(cfg.Address, tmCfg) + if err != nil { + return err + } + + s.listener = listener + var h http.Handler = s.Router + + if cfg.EnableUnsafeCORS { + return tmrpcserver.Serve(s.listener, handlers.CORS()(h), s.logger, tmCfg) + } + + return tmrpcserver.Serve(s.listener, s.Router, s.logger, tmCfg) +} + +func (s *Server) registerSwaggerUI() { + statikFS, err := fs.New() + if err != nil { + panic(err) + } + + staticServer := http.FileServer(statikFS) + s.Router.PathPrefix("/").Handler(staticServer) +} diff --git a/server/config/config.go b/server/config/config.go index cba6917de..94295c683 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -19,6 +19,10 @@ type BaseConfig struct { // specified in this config (e.g. 0.25token1;0.0001token2). MinGasPrices string `mapstructure:"minimum-gas-prices"` + Pruning string `mapstructure:"pruning"` + PruningKeepEvery string `mapstructure:"pruning-keep-every"` + PruningSnapshotEvery string `mapstructure:"pruning-snapshot-every"` + // HaltHeight contains a non-zero block height at which a node will gracefully // halt and shutdown that can be used to assist upgrades and testing. // @@ -34,15 +38,44 @@ type BaseConfig struct { // InterBlockCache enables inter-block caching. InterBlockCache bool `mapstructure:"inter-block-cache"` +} - Pruning string `mapstructure:"pruning"` - PruningKeepEvery string `mapstructure:"pruning-keep-every"` - PruningSnapshotEvery string `mapstructure:"pruning-snapshot-every"` +// APIConfig defines the API listener configuration. +type APIConfig struct { + // Enable defines if the API server should be enabled. + Enable bool `mapstructure:"enable"` + + // Swagger defines if swagger documentation should automatically be registered. + Swagger bool `mapstructure:"swagger"` + + // EnableUnsafeCORS defines if CORS should be enabled (unsafe - use it at your own risk) + EnableUnsafeCORS bool `mapstructure:"enabled-unsafe-cors"` + + // Address defines the API server to listen on + Address string `mapstructure:"address"` + + // MaxOpenConnections defines the number of maximum open connections + MaxOpenConnections uint `mapstructure:"max-open-connections"` + + // RPCReadTimeout defines the Tendermint RPC read timeout (in seconds) + RPCReadTimeout uint `mapstructure:"rpc-read-timeout"` + + // RPCWriteTimeout defines the Tendermint RPC write timeout (in seconds) + RPCWriteTimeout uint `mapstructure:"rpc-write-timeout"` + + // RPCMaxBodyBytes defines the Tendermint maximum response body (in bytes) + RPCMaxBodyBytes uint `mapstructure:"rpc-max-body-bytes"` + + // TODO: TLS/Proxy configuration. + // + // Ref: https://github.com/cosmos/cosmos-sdk/issues/6420 } // Config defines the server's top level configuration type Config struct { BaseConfig `mapstructure:",squash"` + + API APIConfig `mapstructure:"api"` } // SetMinGasPrices sets the validator's minimum gas prices. @@ -75,12 +108,20 @@ func (c *Config) GetMinGasPrices() sdk.DecCoins { // DefaultConfig returns server's default configuration. func DefaultConfig() *Config { return &Config{ - BaseConfig{ + BaseConfig: BaseConfig{ MinGasPrices: defaultMinGasPrices, InterBlockCache: true, Pruning: store.PruningStrategySyncable, PruningKeepEvery: "0", PruningSnapshotEvery: "0", }, + API: APIConfig{ + Enable: false, + Swagger: false, + Address: "tcp://0.0.0.0:1317", + MaxOpenConnections: 1000, + RPCReadTimeout: 10, + RPCMaxBodyBytes: 1000000, + }, } } diff --git a/server/config/toml.go b/server/config/toml.go index d21bc0b5d..014dbe764 100644 --- a/server/config/toml.go +++ b/server/config/toml.go @@ -11,13 +11,26 @@ import ( const defaultConfigTemplate = `# This is a TOML config file. # For more information, see https://github.com/toml-lang/toml -##### main base config options ##### +############################################################################### +### Base Configuration ### +############################################################################### # The minimum gas prices a validator is willing to accept for processing a # transaction. A transaction's fees must meet the minimum of any denomination # specified in this config (e.g. 0.25token1;0.0001token2). minimum-gas-prices = "{{ .BaseConfig.MinGasPrices }}" +# Pruning sets the pruning strategy: syncable, nothing, everything, custom +# syncable: only those states not needed for state syncing will be deleted (keeps last 100 + every 10000th) +# nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node) +# everything: all saved states will be deleted, storing only the current state +# custom: allows fine-grained control through the pruning-keep-every and pruning-snapshot-every options. +pruning = "{{ .BaseConfig.Pruning }}" + +# These are applied if and only if the pruning strategy is custom. +pruning-keep-every = "{{ .BaseConfig.PruningKeepEvery }}" +pruning-snapshot-every = "{{ .BaseConfig.PruningSnapshotEvery }}" + # HaltHeight contains a non-zero block height at which a node will gracefully # halt and shutdown that can be used to assist upgrades and testing. # @@ -34,16 +47,35 @@ halt-time = {{ .BaseConfig.HaltTime }} # InterBlockCache enables inter-block caching. inter-block-cache = {{ .BaseConfig.InterBlockCache }} -# Pruning sets the pruning strategy: syncable, nothing, everything, custom -# syncable: only those states not needed for state syncing will be deleted (keeps last 100 + every 10000th) -# nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node) -# everything: all saved states will be deleted, storing only the current state -# custom: allows fine-grained control through the pruning-keep-every and pruning-snapshot-every options. -pruning = "{{ .BaseConfig.Pruning }}" +############################################################################### +### API Configuration ### +############################################################################### -# These are applied if and only if the pruning strategy is custom. -pruning-keep-every = "{{ .BaseConfig.PruningKeepEvery }}" -pruning-snapshot-every = "{{ .BaseConfig.PruningSnapshotEvery }}" +[api] + +# Enable defines if the API server should be enabled. +enable = {{ .API.Enable }} + +# Swagger defines if swagger documentation should automatically be registered. +swagger = {{ .API.Swagger }} + +# Address defines the API server to listen on +address = "{{ .API.Address }}" + +# MaxOpenConnections defines the number of maximum open connections +max-open-connections = {{ .API.MaxOpenConnections }} + +# RPCReadTimeout defines the Tendermint RPC read timeout (in seconds) +rpc-read-timeout = {{ .API.RPCReadTimeout }} + +# RPCWriteTimeout defines the Tendermint RPC write timeout (in seconds) +rpc-write-timeout = {{ .API.RPCWriteTimeout }} + +# RPCMaxBodyBytes defines the Tendermint maximum response body (in bytes) +rpc-max-body-bytes = {{ .API.RPCMaxBodyBytes }} + +# EnableUnsafeCORS defines if CORS should be enabled (unsafe - use it at your own risk) +enabled-unsafe-cors = {{ .API.EnableUnsafeCORS }} ` var configTemplate *template.Template diff --git a/server/constructors.go b/server/constructors.go index 8a3d8b2d5..1b6281943 100644 --- a/server/constructors.go +++ b/server/constructors.go @@ -11,13 +11,23 @@ import ( tmtypes "github.com/tendermint/tendermint/types" dbm "github.com/tendermint/tm-db" + "github.com/cosmos/cosmos-sdk/server/api" sdk "github.com/cosmos/cosmos-sdk/types" ) type ( + // Application defines an application interface that wraps abci.Application. + // The interface defines the necessary contracts to be implemented in order + // to fully bootstrap and start an application. + Application interface { + abci.Application + + RegisterAPIRoutes(*api.Server) + } + // AppCreator is a function that allows us to lazily initialize an // application using various configurations. - AppCreator func(log.Logger, dbm.DB, io.Writer) abci.Application + AppCreator func(log.Logger, dbm.DB, io.Writer) Application // AppExporter is a function that dumps all app state to // JSON-serializable structure and returns the current validator set. diff --git a/server/start.go b/server/start.go index ba3a15ad5..b57054a81 100644 --- a/server/start.go +++ b/server/start.go @@ -16,6 +16,12 @@ import ( "github.com/tendermint/tendermint/p2p" pvm "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" + "github.com/tendermint/tendermint/rpc/client/local" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/server/api" + "github.com/cosmos/cosmos-sdk/server/config" ) // Tendermint full-node start flags @@ -36,7 +42,7 @@ const ( // StartCmd runs the service passed in, either stand-alone or in-process with // Tendermint. -func StartCmd(ctx *Context, appCreator AppCreator) *cobra.Command { +func StartCmd(ctx *Context, cdc codec.JSONMarshaler, appCreator AppCreator) *cobra.Command { cmd := &cobra.Command{ Use: "start", Short: "Run the full node", @@ -72,7 +78,7 @@ which accepts a path for the resulting pprof file. ctx.Logger.Info("starting ABCI with Tendermint") - err := startInProcess(ctx, appCreator) + err := startInProcess(ctx, cdc, appCreator) return err }, } @@ -143,7 +149,7 @@ func startStandAlone(ctx *Context, appCreator AppCreator) error { select {} } -func startInProcess(ctx *Context, appCreator AppCreator) error { +func startInProcess(ctx *Context, cdc codec.JSONMarshaler, appCreator AppCreator) error { cfg := ctx.Config home := cfg.RootDir @@ -165,13 +171,15 @@ func startInProcess(ctx *Context, appCreator AppCreator) error { return err } + genDocProvider := node.DefaultGenesisDocProviderFunc(cfg) + // create & start tendermint node tmNode, err := node.NewNode( cfg, pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()), nodeKey, proxy.NewLocalClientCreator(app), - node.DefaultGenesisDocProviderFunc(cfg), + genDocProvider, node.DefaultDBProvider, node.DefaultMetricsProvider(cfg.Instrumentation), ctx.Logger.With("module", "node"), @@ -184,6 +192,39 @@ func startInProcess(ctx *Context, appCreator AppCreator) error { return err } + if viper.GetBool("api.enable") { + genDoc, err := genDocProvider() + if err != nil { + return err + } + + // TODO: Since this is running in process, do we need to provide a verifier + // and set TrustNode=false? If so, we need to add additional logic that + // waits for a block to be committed first before starting the API server. + ctx := client.Context{}. + WithHomeDir(home). + WithChainID(genDoc.ChainID). + WithJSONMarshaler(cdc). + WithClient(local.New(tmNode)). + WithTrustNode(true) + + apiSrv := api.New(ctx) + apiCfg := config.APIConfig{ + Address: viper.GetString("api.address"), + MaxOpenConnections: viper.GetUint("api.max-open-connections"), + RPCReadTimeout: viper.GetUint("api.rpc-read-timeout"), + RPCWriteTimeout: viper.GetUint("api.rpc-write-timeout"), + RPCMaxBodyBytes: viper.GetUint("api.rpc-max-body-bytes"), + EnableUnsafeCORS: viper.GetBool("api.enabled-unsafe-cors"), + } + + app.RegisterAPIRoutes(apiSrv) + + if err := apiSrv.Start(apiCfg); err != nil { + return err + } + } + var cpuProfileCleanup func() if cpuProfile := viper.GetString(flagCPUProfile); cpuProfile != "" { diff --git a/server/start_test.go b/server/start_test.go index c1d177c6f..1a229f517 100644 --- a/server/start_test.go +++ b/server/start_test.go @@ -71,7 +71,7 @@ func TestPruningOptions(t *testing.T) { t.Run(tt.name, func(t *testing.T) { viper.Reset() viper.SetDefault(flagPruning, "syncable") - startCommand := StartCmd(nil, nil) + startCommand := StartCmd(nil, nil, nil) tt.paramInit() err := startCommand.PreRunE(startCommand, nil) diff --git a/server/util.go b/server/util.go index a34c59c0e..69c8f1f41 100644 --- a/server/util.go +++ b/server/util.go @@ -122,9 +122,8 @@ func interceptLoadConfig() (conf *cfg.Config, err error) { // add server commands func AddCommands( - ctx *Context, cdc codec.JSONMarshaler, - rootCmd *cobra.Command, - appCreator AppCreator, appExport AppExporter) { + ctx *Context, cdc codec.JSONMarshaler, rootCmd *cobra.Command, appCreator AppCreator, appExport AppExporter, +) { rootCmd.PersistentFlags().String("log_level", ctx.Config.LogLevel, "Log level") @@ -141,7 +140,7 @@ func AddCommands( ) rootCmd.AddCommand( - StartCmd(ctx, appCreator), + StartCmd(ctx, cdc, appCreator), UnsafeResetAllCmd(ctx), flags.LineBreak, tendermintCmd, diff --git a/simapp/app.go b/simapp/app.go index 4825e949c..da8a19b07 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -10,15 +10,18 @@ import ( dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/testdata" "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/server/api" "github.com/cosmos/cosmos-sdk/std" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth/ante" + authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" "github.com/cosmos/cosmos-sdk/x/bank" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" @@ -499,6 +502,14 @@ func (app *SimApp) SimulationManager() *module.SimulationManager { return app.sm } +// RegisterAPIRoutes registers all application module routes with the provided +// API server. +func (app *SimApp) RegisterAPIRoutes(apiSvr *api.Server) { + rpc.RegisterRoutes(apiSvr.ClientCtx, apiSvr.Router) + authrest.RegisterTxRoutes(apiSvr.ClientCtx, apiSvr.Router) + ModuleBasics.RegisterRESTRoutes(apiSvr.ClientCtx, apiSvr.Router) +} + // GetMaccPerms returns a copy of the module account permissions func GetMaccPerms() map[string][]string { dupMaccPerms := make(map[string][]string) diff --git a/simapp/cmd/simcli/main.go b/simapp/cmd/simcli/main.go index ee849b918..b7df81614 100644 --- a/simapp/cmd/simcli/main.go +++ b/simapp/cmd/simcli/main.go @@ -12,14 +12,12 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "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/codec" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" authclient "github.com/cosmos/cosmos-sdk/x/auth/client" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" - authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" "github.com/cosmos/cosmos-sdk/x/auth/types" bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" ) @@ -65,7 +63,6 @@ func main() { queryCmd(cdc), txCmd(cdc), flags.LineBreak, - lcd.ServeCommand(cdc, registerRoutes), flags.LineBreak, keys.Commands(), flags.LineBreak, @@ -148,14 +145,6 @@ func txCmd(cdc *codec.Codec) *cobra.Command { return txCmd } -// registerRoutes registers the routes from the different modules for the REST client. -// NOTE: details on the routes added for each module are in the module documentation -func registerRoutes(rs *lcd.RestServer) { - rpc.RegisterRoutes(rs.ClientCtx, rs.Mux) - authrest.RegisterTxRoutes(rs.ClientCtx, rs.Mux) - simapp.ModuleBasics.RegisterRESTRoutes(rs.ClientCtx, rs.Mux) -} - func initConfig(cmd *cobra.Command) error { home, err := cmd.PersistentFlags().GetString(cli.HomeFlag) if err != nil { diff --git a/simapp/cmd/simd/main.go b/simapp/cmd/simd/main.go index 646595a33..9590650d0 100644 --- a/simapp/cmd/simd/main.go +++ b/simapp/cmd/simd/main.go @@ -71,7 +71,7 @@ func main() { } } -func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application { +func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) server.Application { var cache sdk.MultiStorePersistentCache if viper.GetBool(server.FlagInterBlockCache) { diff --git a/simapp/cmd/simd/testnet.go b/simapp/cmd/simd/testnet.go index 96b94e2df..2a8d1ea60 100644 --- a/simapp/cmd/simd/testnet.go +++ b/simapp/cmd/simd/testnet.go @@ -121,6 +121,7 @@ func InitTestnet( simappConfig := srvconfig.DefaultConfig() simappConfig.MinGasPrices = minGasPrices + simappConfig.API.Enable = true var ( genAccounts []authtypes.GenesisAccount