diff --git a/UPGRADING.md b/UPGRADING.md index a6cab531955a..e8693d50141a 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -108,7 +108,104 @@ Additionally, thanks to the genesis simplification, as explained in [the genesis ##### GRPC-WEB -Grpc-web embedded client has been removed from the server. If you would like to use grpc-web, you can use the [envoy proxy](https://www.envoyproxy.io/docs/envoy/latest/start/start). +Grpc-web embedded client has been removed from the server. If you would like to use grpc-web, you can use the [envoy proxy](https://www.envoyproxy.io/docs/envoy/latest/start/start). Here's how to set it up: + +
+Step by step guide + +1. Install Envoy following the [official installation guide](https://www.envoyproxy.io/docs/envoy/latest/start/install). + +2. Create an Envoy configuration file named `envoy.yaml` with the following content: + + ```yaml + static_resources: + listeners: + - name: listener_0 + address: + socket_address: { address: 0.0.0.0, port_value: 8080 } + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + codec_type: auto + stat_prefix: ingress_http + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: ["*"] + routes: + - match: { prefix: "/" } + route: + cluster: grpc_service + timeout: 0s + max_stream_duration: + grpc_timeout_header_max: 0s + cors: + allow_origin_string_match: + - prefix: "*" + allow_methods: GET, PUT, DELETE, POST, OPTIONS + allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout + max_age: "1728000" + expose_headers: custom-header-1,grpc-status,grpc-message + http_filters: + - name: envoy.filters.http.grpc_web + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb + - name: envoy.filters.http.cors + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - name: grpc_service + connect_timeout: 0.25s + type: logical_dns + http2_protocol_options: {} + lb_policy: round_robin + load_assignment: + cluster_name: cluster_0 + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 0.0.0.0 + port_value: 9090 + ``` + + This configuration tells Envoy to listen on port 8080 and forward requests to your gRPC service on port 9090. Note that this configuration is a starting point and can be modified according to your specific needs and preferences. You may need to adjust ports, addresses, or add additional settings based on your particular setup and requirements. + +3. Start your Cosmos SDK application, ensuring it's configured to serve gRPC on port 9090. + +4. Start Envoy with the configuration file: + + ```bash + envoy -c envoy.yaml + ``` + +5. If Envoy starts successfully, you should see output similar to this: + + ```bash + [2024-08-29 10:47:08.753][6281320][info][config] [source/common/listener_manager/listener_manager_impl.cc:930] all dependencies initialized. starting workers + [2024-08-29 10:47:08.754][6281320][info][main] [source/server/server.cc:978] starting main dispatch loop + ``` + + This indicates that Envoy has started and is ready to proxy requests. + +6. Update your client applications to connect to Envoy (http://localhost:8080 by default). + +
+ +By following these steps, Envoy will handle the translation between gRPC-Web and gRPC, allowing your existing gRPC-Web clients to continue functioning without modifications to your Cosmos SDK application. + +To test the setup, you can use a tool like [grpcurl](https://github.com/fullstorydev/grpcurl). For example: + +```bash +grpcurl -plaintext localhost:8080 cosmos.base.tendermint.v1beta1.Service/GetLatestBlock +``` ##### AnteHandlers diff --git a/server/cmt_cmds.go b/server/cmt_cmds.go index fc4f1c6f562b..1c3b09baeea8 100644 --- a/server/cmt_cmds.go +++ b/server/cmt_cmds.go @@ -363,9 +363,10 @@ func QueryBlockResultsCmd() *cobra.Command { func BootstrapStateCmd[T types.Application](appCreator types.AppCreator[T]) *cobra.Command { cmd := &cobra.Command{ - Use: "bootstrap-state", - Short: "Bootstrap CometBFT state at an arbitrary block height using a light client", - Args: cobra.NoArgs, + Use: "bootstrap-state", + Short: "Bootstrap CometBFT state at an arbitrary block height using a light client", + Args: cobra.NoArgs, + Example: fmt.Sprintf("%s bootstrap-state --height 1000000", version.AppName), RunE: func(cmd *cobra.Command, args []string) error { serverCtx := GetServerContextFromCmd(cmd) logger := log.NewLogger(cmd.OutOrStdout()) diff --git a/server/config/config_test.go b/server/config/config_test.go index 308858d95502..6d827ec27dc7 100644 --- a/server/config/config_test.go +++ b/server/config/config_test.go @@ -10,6 +10,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + pruningtypes "cosmossdk.io/store/pruning/types" + sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -21,15 +23,29 @@ func TestDefaultConfig(t *testing.T) { func TestGetAndSetMinimumGas(t *testing.T) { cfg := DefaultConfig() + // Test case 1: Single coin input := sdk.DecCoins{sdk.NewInt64DecCoin("foo", 5)} cfg.SetMinGasPrices(input) require.Equal(t, "5.000000000000000000foo", cfg.MinGasPrices) require.EqualValues(t, cfg.GetMinGasPrices(), input) + // Test case 2: Multiple coins input = sdk.DecCoins{sdk.NewInt64DecCoin("bar", 1), sdk.NewInt64DecCoin("foo", 5)} cfg.SetMinGasPrices(input) require.Equal(t, "1.000000000000000000bar,5.000000000000000000foo", cfg.MinGasPrices) require.EqualValues(t, cfg.GetMinGasPrices(), input) + + // Test case 4: Empty DecCoins + input = sdk.DecCoins{} + cfg.SetMinGasPrices(input) + require.Equal(t, "", cfg.MinGasPrices) + require.EqualValues(t, cfg.GetMinGasPrices(), input) + + // Test case 5: Invalid string (should panic) + cfg.MinGasPrices = "invalid,gas,prices" + require.Panics(t, func() { + cfg.GetMinGasPrices() + }, "GetMinGasPrices should panic with invalid input") } func TestIndexEventsMarshalling(t *testing.T) { @@ -238,3 +254,40 @@ func TestAppConfig(t *testing.T) { require.NoError(t, v.Unmarshal(appCfg)) require.EqualValues(t, appCfg, defAppConfig) } + +func TestValidateBasic(t *testing.T) { + cfg := DefaultConfig() + + // Test case 1: Valid MinGasPrices + cfg.MinGasPrices = "0.01stake" + err := cfg.ValidateBasic() + require.NoError(t, err) + + // Test case 2: Default configuration (MinGasPrices is empty) + cfg.MinGasPrices = "" + err = cfg.ValidateBasic() + require.Error(t, err) + require.Contains(t, err.Error(), "set min gas price in app.toml or flag or env variable") + + // Test case 3: Invalid pruning and state sync combination + cfg = DefaultConfig() + cfg.MinGasPrices = "0.01stake" + cfg.Pruning = pruningtypes.PruningOptionEverything + cfg.StateSync.SnapshotInterval = 1000 + err = cfg.ValidateBasic() + require.Error(t, err) + require.Contains(t, err.Error(), "cannot enable state sync snapshots with 'everything' pruning setting") +} + +func TestGetConfig(t *testing.T) { + v := viper.New() + v.Set("minimum-gas-prices", "0.01stake") + v.Set("api.enable", true) + v.Set("grpc.max-recv-msg-size", 5*1024*1024) + + cfg, err := GetConfig(v) + require.NoError(t, err) + require.Equal(t, "0.01stake", cfg.MinGasPrices) + require.True(t, cfg.API.Enable) + require.Equal(t, 5*1024*1024, cfg.GRPC.MaxRecvMsgSize) +} diff --git a/server/start.go b/server/start.go index 0392c09b2701..1e565a05cd97 100644 --- a/server/start.go +++ b/server/start.go @@ -126,7 +126,7 @@ type StartCmdOptions[T types.Application] struct { // AddFlags add custom flags to start cmd AddFlags func(cmd *cobra.Command) // StartCommandHandler can be used to customize the start command handler - StartCommandHandler func(svrCtx *Context, clientCtx client.Context, appCreator types.AppCreator[T], inProcessConsensus bool, opts StartCmdOptions[T]) error + StartCommandHandler func(svrCtx *Context, clientCtx client.Context, appCreator types.AppCreator[T], withCMT bool, opts StartCmdOptions[T]) error } // StartCmd runs the service passed in, either stand-alone or in-process with @@ -415,31 +415,28 @@ func getAndValidateConfig(svrCtx *Context) (serverconfig.Config, error) { // getGenDocProvider returns a function which returns the genesis doc from the genesis file. func getGenDocProvider(cfg *cmtcfg.Config) func() (node.ChecksummedGenesisDoc, error) { return func() (node.ChecksummedGenesisDoc, error) { + defaultGenesisDoc := node.ChecksummedGenesisDoc{ + Sha256Checksum: []byte{}, + } + appGenesis, err := genutiltypes.AppGenesisFromFile(cfg.GenesisFile()) if err != nil { - return node.ChecksummedGenesisDoc{ - Sha256Checksum: []byte{}, - }, err + return defaultGenesisDoc, err } gen, err := appGenesis.ToGenesisDoc() if err != nil { - return node.ChecksummedGenesisDoc{ - Sha256Checksum: []byte{}, - }, err + return defaultGenesisDoc, err } + genbz, err := gen.AppState.MarshalJSON() if err != nil { - return node.ChecksummedGenesisDoc{ - Sha256Checksum: []byte{}, - }, err + return defaultGenesisDoc, err } bz, err := json.Marshal(genbz) if err != nil { - return node.ChecksummedGenesisDoc{ - Sha256Checksum: []byte{}, - }, err + return defaultGenesisDoc, err } sum := sha256.Sum256(bz)