Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(simapp/v2): wire grpcgateway server (partial backport #22701) #22713

Merged
merged 2 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 154 additions & 0 deletions server/v2/api/grpcgateway/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package grpcgateway

import (
"context"
"fmt"
"net/http"
"strings"

gateway "github.com/cosmos/gogogateway"
"github.com/cosmos/gogoproto/jsonpb"
"github.com/grpc-ecosystem/grpc-gateway/runtime"

"cosmossdk.io/core/server"
"cosmossdk.io/core/transaction"
"cosmossdk.io/log"
serverv2 "cosmossdk.io/server/v2"

Check failure on line 16 in server/v2/api/grpcgateway/server.go

View workflow job for this annotation

GitHub Actions / split-test-files

no required module provides package cosmossdk.io/server/v2; to add it:

Check failure on line 16 in server/v2/api/grpcgateway/server.go

View workflow job for this annotation

GitHub Actions / dependency-review

no required module provides package cosmossdk.io/server/v2; to add it:

Check failure on line 16 in server/v2/api/grpcgateway/server.go

View workflow job for this annotation

GitHub Actions / dependency-review

no required module provides package cosmossdk.io/server/v2; to add it:

Check failure on line 16 in server/v2/api/grpcgateway/server.go

View workflow job for this annotation

GitHub Actions / dependency-review

could not import cosmossdk.io/server/v2 (invalid package name: "")
)

var (
_ serverv2.ServerComponent[transaction.Tx] = (*Server[transaction.Tx])(nil)
_ serverv2.HasConfig = (*Server[transaction.Tx])(nil)
)

const ServerName = "grpc-gateway"

type Server[T transaction.Tx] struct {
logger log.Logger
config *Config

Check failure on line 28 in server/v2/api/grpcgateway/server.go

View workflow job for this annotation

GitHub Actions / dependency-review

undefined: Config
cfgOptions []CfgOption

Check failure on line 29 in server/v2/api/grpcgateway/server.go

View workflow job for this annotation

GitHub Actions / dependency-review

undefined: CfgOption

server *http.Server
GRPCGatewayRouter *runtime.ServeMux
}

// New creates a new gRPC-gateway server.
func New[T transaction.Tx](
logger log.Logger,
config server.ConfigMap,
ir jsonpb.AnyResolver,
cfgOptions ...CfgOption,

Check failure on line 40 in server/v2/api/grpcgateway/server.go

View workflow job for this annotation

GitHub Actions / dependency-review

undefined: CfgOption
) (*Server[T], error) {
// The default JSON marshaller used by the gRPC-Gateway is unable to marshal non-nullable non-scalar fields.
// Using the gogo/gateway package with the gRPC-Gateway WithMarshaler option fixes the scalar field marshaling issue.
marshalerOption := &gateway.JSONPb{
EmitDefaults: true,
Indent: "",
OrigName: true,
AnyResolver: ir,
}

s := &Server[T]{
GRPCGatewayRouter: runtime.NewServeMux(
// Custom marshaler option is required for gogo proto
runtime.WithMarshalerOption(runtime.MIMEWildcard, marshalerOption),

// This is necessary to get error details properly
// marshaled in unary requests.
runtime.WithProtoErrorHandler(runtime.DefaultHTTPProtoErrorHandler),

// Custom header matcher for mapping request headers to
// GRPC metadata
runtime.WithIncomingHeaderMatcher(CustomGRPCHeaderMatcher),
),
cfgOptions: cfgOptions,
}

serverCfg := s.Config().(*Config)

Check failure on line 67 in server/v2/api/grpcgateway/server.go

View workflow job for this annotation

GitHub Actions / dependency-review

undefined: Config
if len(config) > 0 {
if err := serverv2.UnmarshalSubConfig(config, s.Name(), &serverCfg); err != nil {
return s, fmt.Errorf("failed to unmarshal config: %w", err)
}
}

// TODO: register the gRPC-Gateway routes

s.logger = logger.With(log.ModuleKey, s.Name())
s.config = serverCfg

return s, nil
}

// NewWithConfigOptions creates a new gRPC-gateway server with the provided config options.
func NewWithConfigOptions[T transaction.Tx](opts ...CfgOption) *Server[T] {

Check failure on line 83 in server/v2/api/grpcgateway/server.go

View workflow job for this annotation

GitHub Actions / dependency-review

undefined: CfgOption
return &Server[T]{
cfgOptions: opts,
}
}

func (s *Server[T]) Name() string {
return ServerName
}

func (s *Server[T]) Config() any {
if s.config == nil || s.config.Address == "" {
cfg := DefaultConfig()

Check failure on line 95 in server/v2/api/grpcgateway/server.go

View workflow job for this annotation

GitHub Actions / dependency-review

undefined: DefaultConfig
// overwrite the default config with the provided options
for _, opt := range s.cfgOptions {
opt(cfg)
}

return cfg
}

return s.config
}

func (s *Server[T]) Start(ctx context.Context) error {
if !s.config.Enable {
s.logger.Info(fmt.Sprintf("%s server is disabled via config", s.Name()))
return nil
}

mux := http.NewServeMux()
mux.Handle("/", s.GRPCGatewayRouter)

s.server = &http.Server{
Addr: s.config.Address,
Handler: mux,
}

s.logger.Info("starting gRPC-Gateway server...", "address", s.config.Address)
if err := s.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
return fmt.Errorf("failed to start gRPC-Gateway server: %w", err)
}

return nil
}

func (s *Server[T]) Stop(ctx context.Context) error {
if !s.config.Enable {
return nil
}

s.logger.Info("stopping gRPC-Gateway server...", "address", s.config.Address)
return s.server.Shutdown(ctx)
}

// CustomGRPCHeaderMatcher for mapping request headers to
// GRPC metadata.
// HTTP headers that start with 'Grpc-Metadata-' are automatically mapped to
// gRPC metadata after removing prefix 'Grpc-Metadata-'. We can use this
// CustomGRPCHeaderMatcher if headers don't start with `Grpc-Metadata-`
func CustomGRPCHeaderMatcher(key string) (string, bool) {
// GRPCBlockHeightHeader is the gRPC header for block height.
const GRPCBlockHeightHeader = "x-cosmos-block-height"

switch strings.ToLower(key) {
case GRPCBlockHeightHeader:
return GRPCBlockHeightHeader, true

default:
return runtime.DefaultHeaderMatcher(key)
}
}
20 changes: 20 additions & 0 deletions simapp/v2/simdv2/cmd/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
runtimev2 "cosmossdk.io/runtime/v2"
serverv2 "cosmossdk.io/server/v2"
grpcserver "cosmossdk.io/server/v2/api/grpc"
"cosmossdk.io/server/v2/api/grpcgateway"
"cosmossdk.io/server/v2/api/rest"
"cosmossdk.io/server/v2/api/telemetry"
"cosmossdk.io/server/v2/cometbft"
Expand All @@ -26,6 +27,7 @@
"github.com/cosmos/cosmos-sdk/client/rpc"
sdktelemetry "github.com/cosmos/cosmos-sdk/telemetry"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/version"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
"github.com/cosmos/cosmos-sdk/x/genutil"
Expand Down Expand Up @@ -85,6 +87,7 @@
&serverstore.Server[T]{},
&telemetry.Server[T]{},
&rest.Server[T]{},
&grpcgateway.Server[T]{},
)
}

Expand Down Expand Up @@ -142,6 +145,22 @@
return nil, err
}

grpcgatewayServer, err := grpcgateway.New[T](
logger,
deps.GlobalConfig,
simApp.InterfaceRegistry(),
)
if err != nil {
return nil, err
}

for _, mod := range deps.ModuleManager.Modules() {
if gmod, ok := mod.(module.HasGRPCGateway); ok {
// TODO(@julienrbrt) https://github.com/cosmos/cosmos-sdk/pull/22701#pullrequestreview-2470651390
gmod.RegisterGRPCGatewayRoutes(deps.ClientContext, grpcgatewayServer.GRPCGatewayRouter)
}
}
Comment on lines +157 to +162

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism

// wire server commands
return serverv2.AddCommands[T](
rootCmd,
Expand All @@ -154,6 +173,7 @@
storeComponent,
telemetryServer,
restServer,
grpcgatewayServer,
)
}

Expand Down
11 changes: 10 additions & 1 deletion simapp/v2/simdv2/cmd/testnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
runtimev2 "cosmossdk.io/runtime/v2"
serverv2 "cosmossdk.io/server/v2"
"cosmossdk.io/server/v2/api/grpc"
"cosmossdk.io/server/v2/api/grpcgateway"
"cosmossdk.io/server/v2/api/rest"
"cosmossdk.io/server/v2/cometbft"
"cosmossdk.io/server/v2/store"
Expand Down Expand Up @@ -193,7 +194,9 @@
for i := 0; i < args.numValidators; i++ {
var portOffset int
grpcConfig := grpc.DefaultConfig()
grpcgatewayConfig := grpcgateway.DefaultConfig()
Fixed Show fixed Hide fixed
restConfig := rest.DefaultConfig()

if args.singleMachine {
portOffset = i
p2pPortStart = 16656 // use different start point to not conflict with rpc port
Expand All @@ -208,6 +211,11 @@
MaxSendMsgSize: grpc.DefaultConfig().MaxSendMsgSize,
}

grpcgatewayConfig = &grpcgateway.Config{
Fixed Show fixed Hide fixed
Enable: true,
Address: fmt.Sprintf("127.0.0.1:%d", apiPort+portOffset),
}

restConfig = &rest.Config{
Enable: true,
Address: fmt.Sprintf("127.0.0.1:%d", restPort+portOffset),
Expand Down Expand Up @@ -345,8 +353,9 @@
cometServer := cometbft.NewWithConfigOptions[T](cometbft.OverwriteDefaultConfigTomlConfig(nodeConfig))
storeServer := &store.Server[T]{}
grpcServer := grpc.NewWithConfigOptions[T](grpc.OverwriteDefaultConfig(grpcConfig))
grpcgatewayServer := grpcgateway.NewWithConfigOptions[T](grpcgateway.OverwriteDefaultConfig(grpcgatewayConfig))
restServer := rest.NewWithConfigOptions[T](rest.OverwriteDefaultConfig(restConfig))
server := serverv2.NewServer[T](serverCfg, cometServer, storeServer, grpcServer, restServer)
server := serverv2.NewServer[T](serverCfg, cometServer, storeServer, grpcServer, grpcgatewayServer, restServer)
err = server.WriteConfig(filepath.Join(nodeDir, "config"))
if err != nil {
return err
Expand Down
Loading