refactor(server/v2): eager config loading (#22267)
This commit is contained in:
+1
-1
@@ -25,7 +25,7 @@
|
||||
/runtime/v2/ @julienrbrt @hieuvubk @cosmos/sdk-core-dev
|
||||
/schema/ @aaronc @testinginprod @cosmos/sdk-core-dev
|
||||
/server/ @cosmos/sdk-core-dev
|
||||
/server/v2/ @julienrbrt @hieuvubk @cosmos/sdk-core-dev
|
||||
/server/v2/ @julienrbrt @hieuvubk @kocubinski @cosmos/sdk-core-dev
|
||||
/server/v2/stf/ @testinginprod @kocubinski @cosmos/sdk-core-dev
|
||||
/server/v2/appmanager/ @testinginprod @facundomedica @cosmos/sdk-core-dev
|
||||
/server/v2/cometbft/ @facundomedica @sontrinh16 @cosmos/sdk-core-dev
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"cosmossdk.io/core/server"
|
||||
"cosmossdk.io/depinject"
|
||||
)
|
||||
|
||||
// ModuleConfigMaps is a map module scoped ConfigMaps
|
||||
type ModuleConfigMaps map[string]server.ConfigMap
|
||||
|
||||
type ModuleConfigMapsInput struct {
|
||||
depinject.In
|
||||
|
||||
ModuleConfigs []server.ModuleConfigMap
|
||||
DynamicConfig server.DynamicConfig `optional:"true"`
|
||||
}
|
||||
|
||||
// ProvideModuleConfigMaps returns a map of module name to module config map.
|
||||
// The module config map is a map of flag to value.
|
||||
func ProvideModuleConfigMaps(in ModuleConfigMapsInput) ModuleConfigMaps {
|
||||
moduleConfigMaps := make(ModuleConfigMaps)
|
||||
if in.DynamicConfig == nil {
|
||||
return moduleConfigMaps
|
||||
}
|
||||
for _, moduleConfig := range in.ModuleConfigs {
|
||||
cfg := moduleConfig.Config
|
||||
name := moduleConfig.Module
|
||||
moduleConfigMaps[name] = make(server.ConfigMap)
|
||||
for flag, df := range cfg {
|
||||
val := in.DynamicConfig.Get(flag)
|
||||
if val != nil {
|
||||
moduleConfigMaps[name][flag] = val
|
||||
} else {
|
||||
moduleConfigMaps[name][flag] = df
|
||||
}
|
||||
}
|
||||
}
|
||||
return moduleConfigMaps
|
||||
}
|
||||
|
||||
func ProvideModuleScopedConfigMap(
|
||||
key depinject.ModuleKey,
|
||||
moduleConfigs ModuleConfigMaps,
|
||||
) server.ConfigMap {
|
||||
return moduleConfigs[key.Name()]
|
||||
}
|
||||
@@ -103,6 +103,8 @@ func init() {
|
||||
ProvideTransientStoreService,
|
||||
ProvideModuleManager,
|
||||
ProvideCometService,
|
||||
ProvideModuleConfigMaps,
|
||||
ProvideModuleScopedConfigMap,
|
||||
),
|
||||
appconfig.Invoke(SetupAppBuilder),
|
||||
)
|
||||
|
||||
+1
-1
@@ -83,7 +83,7 @@ func (a *App[T]) LoadLatestHeight() (uint64, error) {
|
||||
return a.db.GetLatestVersion()
|
||||
}
|
||||
|
||||
// GetQueryHandlers returns the query handlers.
|
||||
// QueryHandlers returns the query handlers.
|
||||
func (a *App[T]) QueryHandlers() map[string]appmodulev2.Handler {
|
||||
return a.queryHandlers
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
type AppBuilder[T transaction.Tx] struct {
|
||||
app *App[T]
|
||||
storeBuilder root.Builder
|
||||
storeConfig *root.Config
|
||||
|
||||
// the following fields are used to overwrite the default
|
||||
branch func(state store.ReaderMap) store.WriterMap
|
||||
@@ -82,12 +83,13 @@ func (a *AppBuilder[T]) Build(opts ...AppBuilderOption[T]) (*App[T], error) {
|
||||
}
|
||||
}
|
||||
|
||||
a.app.db = a.storeBuilder.Get()
|
||||
if a.app.db == nil {
|
||||
return nil, fmt.Errorf("storeBuilder did not return a db")
|
||||
var err error
|
||||
a.app.db, err = a.storeBuilder.Build(a.app.logger, a.storeConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := a.app.moduleManager.RegisterServices(a.app); err != nil {
|
||||
if err = a.app.moduleManager.RegisterServices(a.app); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"cosmossdk.io/core/server"
|
||||
"cosmossdk.io/depinject"
|
||||
)
|
||||
|
||||
// GlobalConfig is a recursive configuration map containing configuration
|
||||
// key-value pairs parsed from the configuration file, flags, or other
|
||||
// input sources.
|
||||
//
|
||||
// It is aliased to server.ConfigMap so that DI can distinguish between
|
||||
// module-scoped and global configuration maps. In the DI container `server.ConfigMap`
|
||||
// objects are module-scoped and `GlobalConfig` is global-scoped.
|
||||
type GlobalConfig server.ConfigMap
|
||||
|
||||
// ModuleConfigMaps is a map module scoped ConfigMaps
|
||||
type ModuleConfigMaps map[string]server.ConfigMap
|
||||
|
||||
// ProvideModuleConfigMaps returns a map of module name to module config map.
|
||||
// The module config map is a map of flag to value.
|
||||
func ProvideModuleConfigMaps(
|
||||
moduleConfigs []server.ModuleConfigMap,
|
||||
globalConfig GlobalConfig,
|
||||
) ModuleConfigMaps {
|
||||
moduleConfigMaps := make(ModuleConfigMaps)
|
||||
for _, moduleConfig := range moduleConfigs {
|
||||
cfg := moduleConfig.Config
|
||||
name := moduleConfig.Module
|
||||
moduleConfigMaps[name] = make(server.ConfigMap)
|
||||
for flag, df := range cfg {
|
||||
m := globalConfig
|
||||
fetchFlag := flag
|
||||
// splitting on "." is required to handle nested flags which are defined
|
||||
// in other modules that are not the current module
|
||||
// for example: "server.minimum-gas-prices" is defined in the server module
|
||||
// but required by x/validate
|
||||
for _, part := range strings.Split(flag, ".") {
|
||||
if maybeMap, ok := m[part]; ok {
|
||||
innerMap, ok := maybeMap.(map[string]any)
|
||||
if !ok {
|
||||
fetchFlag = part
|
||||
break
|
||||
}
|
||||
m = innerMap
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if val, ok := m[fetchFlag]; ok {
|
||||
moduleConfigMaps[name][flag] = val
|
||||
} else {
|
||||
moduleConfigMaps[name][flag] = df
|
||||
}
|
||||
}
|
||||
}
|
||||
return moduleConfigMaps
|
||||
}
|
||||
|
||||
func ProvideModuleScopedConfigMap(
|
||||
key depinject.ModuleKey,
|
||||
moduleConfigs ModuleConfigMaps,
|
||||
) server.ConfigMap {
|
||||
return moduleConfigs[key.Name()]
|
||||
}
|
||||
@@ -99,6 +99,8 @@ func init() {
|
||||
ProvideModuleManager[transaction.Tx],
|
||||
ProvideEnvironment,
|
||||
ProvideKVService,
|
||||
ProvideModuleConfigMaps,
|
||||
ProvideModuleScopedConfigMap,
|
||||
),
|
||||
appconfig.Invoke(SetupAppBuilder),
|
||||
)
|
||||
@@ -108,6 +110,7 @@ func ProvideAppBuilder[T transaction.Tx](
|
||||
interfaceRegistrar registry.InterfaceRegistrar,
|
||||
amino registry.AminoRegistrar,
|
||||
storeBuilder root.Builder,
|
||||
storeConfig *root.Config,
|
||||
) (
|
||||
*AppBuilder[T],
|
||||
*stf.MsgRouterBuilder,
|
||||
@@ -134,7 +137,7 @@ func ProvideAppBuilder[T transaction.Tx](
|
||||
queryHandlers: map[string]appmodulev2.Handler{},
|
||||
storeLoader: DefaultStoreLoader,
|
||||
}
|
||||
appBuilder := &AppBuilder[T]{app: app, storeBuilder: storeBuilder}
|
||||
appBuilder := &AppBuilder[T]{app: app, storeBuilder: storeBuilder, storeConfig: storeConfig}
|
||||
|
||||
return appBuilder, msgRouterBuilder, appModule[T]{app}, protoFiles, protoTypes
|
||||
}
|
||||
@@ -142,6 +145,7 @@ func ProvideAppBuilder[T transaction.Tx](
|
||||
type AppInputs struct {
|
||||
depinject.In
|
||||
|
||||
StoreConfig *root.Config
|
||||
Config *runtimev2.Module
|
||||
AppBuilder *AppBuilder[transaction.Tx]
|
||||
ModuleManager *MM[transaction.Tx]
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
|
||||
appmodulev2 "cosmossdk.io/core/appmodule/v2"
|
||||
"cosmossdk.io/core/server"
|
||||
"cosmossdk.io/core/transaction"
|
||||
"cosmossdk.io/log"
|
||||
serverv2 "cosmossdk.io/server/v2"
|
||||
@@ -42,40 +43,50 @@ type Server[T transaction.Tx] struct {
|
||||
}
|
||||
|
||||
// New creates a new grpc server.
|
||||
func New[T transaction.Tx](cfgOptions ...CfgOption) *Server[T] {
|
||||
return &Server[T]{
|
||||
func New[T transaction.Tx](
|
||||
logger log.Logger,
|
||||
interfaceRegistry server.InterfaceRegistry,
|
||||
queryHandlers map[string]appmodulev2.Handler,
|
||||
queryable interface {
|
||||
Query(ctx context.Context, version uint64, msg transaction.Msg) (transaction.Msg, error)
|
||||
},
|
||||
cfg server.ConfigMap,
|
||||
cfgOptions ...CfgOption,
|
||||
) (*Server[T], error) {
|
||||
srv := &Server[T]{
|
||||
cfgOptions: cfgOptions,
|
||||
}
|
||||
}
|
||||
|
||||
// Init returns a correctly configured and initialized gRPC server.
|
||||
// Note, the caller is responsible for starting the server.
|
||||
func (s *Server[T]) Init(appI serverv2.AppI[T], cfg map[string]any, logger log.Logger) error {
|
||||
serverCfg := s.Config().(*Config)
|
||||
serverCfg := srv.Config().(*Config)
|
||||
if len(cfg) > 0 {
|
||||
if err := serverv2.UnmarshalSubConfig(cfg, s.Name(), &serverCfg); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal config: %w", err)
|
||||
if err := serverv2.UnmarshalSubConfig(cfg, srv.Name(), &serverCfg); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
|
||||
}
|
||||
}
|
||||
methodsMap := appI.QueryHandlers()
|
||||
|
||||
grpcSrv := grpc.NewServer(
|
||||
grpc.ForceServerCodec(newProtoCodec(appI.InterfaceRegistry()).GRPCCodec()),
|
||||
grpc.ForceServerCodec(newProtoCodec(interfaceRegistry).GRPCCodec()),
|
||||
grpc.MaxSendMsgSize(serverCfg.MaxSendMsgSize),
|
||||
grpc.MaxRecvMsgSize(serverCfg.MaxRecvMsgSize),
|
||||
grpc.UnknownServiceHandler(
|
||||
makeUnknownServiceHandler(methodsMap, appI),
|
||||
),
|
||||
grpc.UnknownServiceHandler(makeUnknownServiceHandler(queryHandlers, queryable)),
|
||||
)
|
||||
|
||||
// Reflection allows external clients to see what services and methods the gRPC server exposes.
|
||||
gogoreflection.Register(grpcSrv, slices.Collect(maps.Keys(methodsMap)), logger.With("sub-module", "grpc-reflection"))
|
||||
gogoreflection.Register(grpcSrv, slices.Collect(maps.Keys(queryHandlers)), logger.With("sub-module", "grpc-reflection"))
|
||||
|
||||
s.grpcSrv = grpcSrv
|
||||
s.config = serverCfg
|
||||
s.logger = logger.With(log.ModuleKey, s.Name())
|
||||
srv.grpcSrv = grpcSrv
|
||||
srv.config = serverCfg
|
||||
srv.logger = logger.With(log.ModuleKey, srv.Name())
|
||||
|
||||
return nil
|
||||
return srv, nil
|
||||
}
|
||||
|
||||
// NewWithConfigOptions creates a new GRPC server with the provided config options.
|
||||
// It is *not* a fully functional server (since it has been created without dependencies)
|
||||
// The returned server should only be used to get and set configuration.
|
||||
func NewWithConfigOptions[T transaction.Tx](opts ...CfgOption) *Server[T] {
|
||||
return &Server[T]{
|
||||
cfgOptions: opts,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server[T]) StartCmdFlags() *pflag.FlagSet {
|
||||
@@ -186,7 +197,7 @@ func (s *Server[T]) Config() any {
|
||||
return s.config
|
||||
}
|
||||
|
||||
func (s *Server[T]) Start(ctx context.Context) error {
|
||||
func (s *Server[T]) Start(context.Context) error {
|
||||
if !s.config.Enable {
|
||||
s.logger.Info(fmt.Sprintf("%s server is disabled via config", s.Name()))
|
||||
return nil
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"cosmossdk.io/core/server"
|
||||
"cosmossdk.io/core/transaction"
|
||||
"cosmossdk.io/log"
|
||||
serverv2 "cosmossdk.io/server/v2"
|
||||
@@ -34,7 +35,13 @@ type Server[T transaction.Tx] struct {
|
||||
}
|
||||
|
||||
// New creates a new gRPC-gateway server.
|
||||
func New[T transaction.Tx](grpcSrv *grpc.Server, ir jsonpb.AnyResolver, cfgOptions ...CfgOption) *Server[T] {
|
||||
func New[T transaction.Tx](
|
||||
logger log.Logger,
|
||||
config server.ConfigMap,
|
||||
grpcSrv *grpc.Server,
|
||||
ir jsonpb.AnyResolver,
|
||||
cfgOptions ...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{
|
||||
@@ -44,7 +51,7 @@ func New[T transaction.Tx](grpcSrv *grpc.Server, ir jsonpb.AnyResolver, cfgOptio
|
||||
AnyResolver: ir,
|
||||
}
|
||||
|
||||
return &Server[T]{
|
||||
s := &Server[T]{
|
||||
gRPCSrv: grpcSrv,
|
||||
gRPCGatewayRouter: runtime.NewServeMux(
|
||||
// Custom marshaler option is required for gogo proto
|
||||
@@ -60,6 +67,20 @@ func New[T transaction.Tx](grpcSrv *grpc.Server, ir jsonpb.AnyResolver, cfgOptio
|
||||
),
|
||||
cfgOptions: cfgOptions,
|
||||
}
|
||||
|
||||
serverCfg := s.Config().(*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
|
||||
}
|
||||
|
||||
func (s *Server[T]) Name() string {
|
||||
@@ -80,22 +101,6 @@ func (s *Server[T]) Config() any {
|
||||
return s.config
|
||||
}
|
||||
|
||||
func (s *Server[T]) Init(appI serverv2.AppI[transaction.Tx], cfg map[string]any, logger log.Logger) error {
|
||||
serverCfg := s.Config().(*Config)
|
||||
if len(cfg) > 0 {
|
||||
if err := serverv2.UnmarshalSubConfig(cfg, s.Name(), &serverCfg); err != nil {
|
||||
return 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 nil
|
||||
}
|
||||
|
||||
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()))
|
||||
|
||||
@@ -6,9 +6,11 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"cosmossdk.io/core/server"
|
||||
"cosmossdk.io/core/transaction"
|
||||
"cosmossdk.io/log"
|
||||
serverv2 "cosmossdk.io/server/v2"
|
||||
"cosmossdk.io/server/v2/appmanager"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -24,9 +26,37 @@ type Server[T transaction.Tx] struct {
|
||||
cfgOptions []CfgOption
|
||||
}
|
||||
|
||||
func New[T transaction.Tx](cfgOptions ...CfgOption) *Server[T] {
|
||||
return &Server[T]{
|
||||
func New[T transaction.Tx](
|
||||
appManager appmanager.AppManager[T],
|
||||
logger log.Logger,
|
||||
cfg server.ConfigMap,
|
||||
cfgOptions ...CfgOption,
|
||||
) (*Server[T], error) {
|
||||
srv := &Server[T]{
|
||||
cfgOptions: cfgOptions,
|
||||
logger: logger.With(log.ModuleKey, ServerName),
|
||||
router: http.NewServeMux(),
|
||||
}
|
||||
|
||||
srv.router.Handle("/", NewDefaultHandler(appManager))
|
||||
|
||||
serverCfg := srv.Config().(*Config)
|
||||
if len(cfg) > 0 {
|
||||
if err := serverv2.UnmarshalSubConfig(cfg, srv.Name(), &serverCfg); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
|
||||
}
|
||||
}
|
||||
srv.config = serverCfg
|
||||
|
||||
return srv, nil
|
||||
}
|
||||
|
||||
// NewWithConfigOptions creates a new REST server with the provided config options.
|
||||
// It is *not* a fully functional server (since it has been created without dependencies)
|
||||
// The returned server should only be used to get and set configuration.
|
||||
func NewWithConfigOptions[T transaction.Tx](opts ...CfgOption) *Server[T] {
|
||||
return &Server[T]{
|
||||
cfgOptions: opts,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,23 +64,6 @@ func (s *Server[T]) Name() string {
|
||||
return ServerName
|
||||
}
|
||||
|
||||
func (s *Server[T]) Init(appI serverv2.AppI[T], cfg map[string]any, logger log.Logger) error {
|
||||
s.logger = logger.With(log.ModuleKey, s.Name())
|
||||
|
||||
serverCfg := s.Config().(*Config)
|
||||
if len(cfg) > 0 {
|
||||
if err := serverv2.UnmarshalSubConfig(cfg, s.Name(), &serverCfg); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
s.router = http.NewServeMux()
|
||||
s.router.Handle("/", NewDefaultHandler(appI))
|
||||
s.config = serverCfg
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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()))
|
||||
|
||||
@@ -17,7 +17,7 @@ func TestServerConfig(t *testing.T) {
|
||||
{
|
||||
name: "Default configuration, no custom configuration",
|
||||
setupFunc: func() *Config {
|
||||
s := New[transaction.Tx]()
|
||||
s := &Server[transaction.Tx]{}
|
||||
return s.Config().(*Config)
|
||||
},
|
||||
expectedConfig: DefaultConfig(),
|
||||
@@ -25,7 +25,7 @@ func TestServerConfig(t *testing.T) {
|
||||
{
|
||||
name: "Custom configuration",
|
||||
setupFunc: func() *Config {
|
||||
s := New[transaction.Tx](func(config *Config) {
|
||||
s := NewWithConfigOptions[transaction.Tx](func(config *Config) {
|
||||
config.Enable = false
|
||||
})
|
||||
return s.Config().(*Config)
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"cosmossdk.io/core/server"
|
||||
"cosmossdk.io/core/transaction"
|
||||
"cosmossdk.io/log"
|
||||
serverv2 "cosmossdk.io/server/v2"
|
||||
@@ -27,8 +28,23 @@ type Server[T transaction.Tx] struct {
|
||||
}
|
||||
|
||||
// New creates a new telemetry server.
|
||||
func New[T transaction.Tx]() *Server[T] {
|
||||
return &Server[T]{}
|
||||
func New[T transaction.Tx](cfg server.ConfigMap, logger log.Logger) (*Server[T], error) {
|
||||
srv := &Server[T]{}
|
||||
serverCfg := srv.Config().(*Config)
|
||||
if len(cfg) > 0 {
|
||||
if err := serverv2.UnmarshalSubConfig(cfg, srv.Name(), &serverCfg); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
|
||||
}
|
||||
}
|
||||
srv.config = serverCfg
|
||||
srv.logger = logger.With(log.ModuleKey, srv.Name())
|
||||
|
||||
metrics, err := NewMetrics(srv.config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize metrics: %w", err)
|
||||
}
|
||||
srv.metrics = metrics
|
||||
return srv, nil
|
||||
}
|
||||
|
||||
// Name returns the server name.
|
||||
@@ -44,26 +60,6 @@ func (s *Server[T]) Config() any {
|
||||
return s.config
|
||||
}
|
||||
|
||||
// Init implements serverv2.ServerComponent.
|
||||
func (s *Server[T]) Init(appI serverv2.AppI[T], cfg map[string]any, logger log.Logger) error {
|
||||
serverCfg := s.Config().(*Config)
|
||||
if len(cfg) > 0 {
|
||||
if err := serverv2.UnmarshalSubConfig(cfg, s.Name(), &serverCfg); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal config: %w", err)
|
||||
}
|
||||
}
|
||||
s.config = serverCfg
|
||||
s.logger = logger.With(log.ModuleKey, s.Name())
|
||||
|
||||
metrics, err := NewMetrics(s.config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize metrics: %w", err)
|
||||
}
|
||||
s.metrics = metrics
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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()))
|
||||
|
||||
@@ -18,12 +18,17 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
appmodulev2 "cosmossdk.io/core/appmodule/v2"
|
||||
"cosmossdk.io/core/server"
|
||||
"cosmossdk.io/core/transaction"
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/schema/decoding"
|
||||
"cosmossdk.io/schema/indexer"
|
||||
serverv2 "cosmossdk.io/server/v2"
|
||||
"cosmossdk.io/server/v2/appmanager"
|
||||
cometlog "cosmossdk.io/server/v2/cometbft/log"
|
||||
"cosmossdk.io/server/v2/cometbft/mempool"
|
||||
"cosmossdk.io/server/v2/cometbft/types"
|
||||
"cosmossdk.io/store/v2/snapshots"
|
||||
|
||||
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
|
||||
@@ -49,34 +54,39 @@ type CometBFTServer[T transaction.Tx] struct {
|
||||
}
|
||||
|
||||
func New[T transaction.Tx](
|
||||
logger log.Logger,
|
||||
appName string,
|
||||
store types.Store,
|
||||
appManager appmanager.AppManager[T],
|
||||
queryHandlers map[string]appmodulev2.Handler,
|
||||
decoderResolver decoding.DecoderResolver,
|
||||
txCodec transaction.Codec[T],
|
||||
cfg server.ConfigMap,
|
||||
serverOptions ServerOptions[T],
|
||||
cfgOptions ...CfgOption,
|
||||
) *CometBFTServer[T] {
|
||||
return &CometBFTServer[T]{
|
||||
) (*CometBFTServer[T], error) {
|
||||
srv := &CometBFTServer[T]{
|
||||
initTxCodec: txCodec,
|
||||
serverOptions: serverOptions,
|
||||
cfgOptions: cfgOptions,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CometBFTServer[T]) Init(appI serverv2.AppI[T], cfg map[string]any, logger log.Logger) error {
|
||||
home, _ := cfg[serverv2.FlagHome].(string)
|
||||
|
||||
// get configs (app.toml + config.toml) from viper
|
||||
appTomlConfig := s.Config().(*AppTomlConfig)
|
||||
appTomlConfig := srv.Config().(*AppTomlConfig)
|
||||
configTomlConfig := cmtcfg.DefaultConfig().SetRoot(home)
|
||||
if len(cfg) > 0 {
|
||||
if err := serverv2.UnmarshalSubConfig(cfg, s.Name(), &appTomlConfig); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal config: %w", err)
|
||||
if err := serverv2.UnmarshalSubConfig(cfg, srv.Name(), &appTomlConfig); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
|
||||
}
|
||||
|
||||
if err := serverv2.UnmarshalSubConfig(cfg, "", &configTomlConfig); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal config: %w", err)
|
||||
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
s.config = Config{
|
||||
srv.config = Config{
|
||||
ConfigTomlConfig: configTomlConfig,
|
||||
AppTomlConfig: appTomlConfig,
|
||||
}
|
||||
@@ -96,59 +106,68 @@ func (s *CometBFTServer[T]) Init(appI serverv2.AppI[T], cfg map[string]any, logg
|
||||
}
|
||||
}
|
||||
|
||||
indexEvents := make(map[string]struct{}, len(s.config.AppTomlConfig.IndexEvents))
|
||||
for _, e := range s.config.AppTomlConfig.IndexEvents {
|
||||
indexEvents := make(map[string]struct{}, len(srv.config.AppTomlConfig.IndexEvents))
|
||||
for _, e := range srv.config.AppTomlConfig.IndexEvents {
|
||||
indexEvents[e] = struct{}{}
|
||||
}
|
||||
|
||||
s.logger = logger.With(log.ModuleKey, s.Name())
|
||||
rs := appI.Store()
|
||||
srv.logger = logger.With(log.ModuleKey, srv.Name())
|
||||
consensus := NewConsensus(
|
||||
s.logger,
|
||||
appI.Name(),
|
||||
appI,
|
||||
appI.Close,
|
||||
s.serverOptions.Mempool(cfg),
|
||||
logger,
|
||||
appName,
|
||||
appManager,
|
||||
nil,
|
||||
srv.serverOptions.Mempool(cfg),
|
||||
indexEvents,
|
||||
appI.QueryHandlers(),
|
||||
rs,
|
||||
s.config,
|
||||
s.initTxCodec,
|
||||
queryHandlers,
|
||||
store,
|
||||
srv.config,
|
||||
srv.initTxCodec,
|
||||
chainID,
|
||||
)
|
||||
consensus.prepareProposalHandler = s.serverOptions.PrepareProposalHandler
|
||||
consensus.processProposalHandler = s.serverOptions.ProcessProposalHandler
|
||||
consensus.checkTxHandler = s.serverOptions.CheckTxHandler
|
||||
consensus.verifyVoteExt = s.serverOptions.VerifyVoteExtensionHandler
|
||||
consensus.extendVote = s.serverOptions.ExtendVoteHandler
|
||||
consensus.addrPeerFilter = s.serverOptions.AddrPeerFilter
|
||||
consensus.idPeerFilter = s.serverOptions.IdPeerFilter
|
||||
consensus.prepareProposalHandler = srv.serverOptions.PrepareProposalHandler
|
||||
consensus.processProposalHandler = srv.serverOptions.ProcessProposalHandler
|
||||
consensus.checkTxHandler = srv.serverOptions.CheckTxHandler
|
||||
consensus.verifyVoteExt = srv.serverOptions.VerifyVoteExtensionHandler
|
||||
consensus.extendVote = srv.serverOptions.ExtendVoteHandler
|
||||
consensus.addrPeerFilter = srv.serverOptions.AddrPeerFilter
|
||||
consensus.idPeerFilter = srv.serverOptions.IdPeerFilter
|
||||
|
||||
ss := rs.GetStateStorage().(snapshots.StorageSnapshotter)
|
||||
sc := rs.GetStateCommitment().(snapshots.CommitSnapshotter)
|
||||
ss := store.GetStateStorage().(snapshots.StorageSnapshotter)
|
||||
sc := store.GetStateCommitment().(snapshots.CommitSnapshotter)
|
||||
|
||||
snapshotStore, err := GetSnapshotStore(s.config.ConfigTomlConfig.RootDir)
|
||||
snapshotStore, err := GetSnapshotStore(srv.config.ConfigTomlConfig.RootDir)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
consensus.snapshotManager = snapshots.NewManager(snapshotStore, s.serverOptions.SnapshotOptions(cfg), sc, ss, nil, s.logger)
|
||||
consensus.snapshotManager = snapshots.NewManager(
|
||||
snapshotStore, srv.serverOptions.SnapshotOptions(cfg), sc, ss, nil, logger)
|
||||
|
||||
srv.Consensus = consensus
|
||||
|
||||
// initialize the indexer
|
||||
if indexerCfg := s.config.AppTomlConfig.Indexer; len(indexerCfg.Target) > 0 {
|
||||
if indexerCfg := srv.config.AppTomlConfig.Indexer; len(indexerCfg.Target) > 0 {
|
||||
listener, err := indexer.StartIndexing(indexer.IndexingOptions{
|
||||
Config: indexerCfg,
|
||||
Resolver: appI.SchemaDecoderResolver(),
|
||||
Logger: s.logger.With(log.ModuleKey, "indexer"),
|
||||
Resolver: decoderResolver,
|
||||
Logger: logger.With(log.ModuleKey, "indexer"),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start indexing: %w", err)
|
||||
return nil, fmt.Errorf("failed to start indexing: %w", err)
|
||||
}
|
||||
consensus.listener = &listener.Listener
|
||||
}
|
||||
|
||||
s.Consensus = consensus
|
||||
return srv, nil
|
||||
}
|
||||
|
||||
return nil
|
||||
// NewWithConfigOptions creates a new CometBFT server with the provided config options.
|
||||
// It is *not* a fully functional server (since it has been created without dependencies)
|
||||
// The returned server should only be used to get and set configuration.
|
||||
func NewWithConfigOptions[T transaction.Tx](opts ...CfgOption) *CometBFTServer[T] {
|
||||
return &CometBFTServer[T]{
|
||||
cfgOptions: opts,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *CometBFTServer[T]) Name() string {
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
package serverv2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"cosmossdk.io/core/server"
|
||||
"cosmossdk.io/log"
|
||||
)
|
||||
|
||||
// CommandFactory is a factory help create server/v2 root commands.
|
||||
// For example usage see simapp/v2/cmd/root_di.go
|
||||
type CommandFactory struct {
|
||||
defaultHomeDir string
|
||||
envPrefix string
|
||||
configWriter ConfigWriter
|
||||
loggerFactory func(server.ConfigMap, io.Writer) (log.Logger, error)
|
||||
|
||||
logger log.Logger
|
||||
// TODO remove this field
|
||||
// this viper handle is kept because certain commands in server/v2 fetch a viper instance
|
||||
// from the command context in order to read the config.
|
||||
// After merging #22267 this is no longer required, and server.ConfigMap can be used instead.
|
||||
// See issue #22388
|
||||
vipr *viper.Viper
|
||||
}
|
||||
|
||||
type CommandFactoryOption func(*CommandFactory) error
|
||||
|
||||
// NewCommandFactory creates a new CommandFactory with the given options.
|
||||
func NewCommandFactory(opts ...CommandFactoryOption) (*CommandFactory, error) {
|
||||
f := &CommandFactory{}
|
||||
for _, opt := range opts {
|
||||
err := opt(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// WithEnvPrefix sets the environment variable prefix for the command factory.
|
||||
func WithEnvPrefix(envPrefix string) CommandFactoryOption {
|
||||
return func(f *CommandFactory) error {
|
||||
f.envPrefix = envPrefix
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithStdDefaultHomeDir sets the server's default home directory `user home directory`/`defaultHomeBasename`.
|
||||
func WithStdDefaultHomeDir(defaultHomeBasename string) CommandFactoryOption {
|
||||
return func(f *CommandFactory) error {
|
||||
// get the home directory from the environment variable
|
||||
// to not clash with the $HOME system variable, when no prefix is set
|
||||
// we check the NODE_HOME environment variable
|
||||
homeDir, envHome := "", "HOME"
|
||||
if len(f.envPrefix) > 0 {
|
||||
homeDir = os.Getenv(f.envPrefix + "_" + envHome)
|
||||
} else {
|
||||
homeDir = os.Getenv("NODE_" + envHome)
|
||||
}
|
||||
if homeDir != "" {
|
||||
f.defaultHomeDir = filepath.Clean(homeDir)
|
||||
return nil
|
||||
}
|
||||
|
||||
// get user home directory
|
||||
userHomeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f.defaultHomeDir = filepath.Join(userHomeDir, defaultHomeBasename)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithDefaultHomeDir sets the server's default home directory.
|
||||
func WithDefaultHomeDir(homedir string) CommandFactoryOption {
|
||||
return func(f *CommandFactory) error {
|
||||
f.defaultHomeDir = homedir
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithConfigWriter sets the config writer for the command factory.
|
||||
// If set the config writer will be used to write TOML config files during ParseCommand invocations.
|
||||
func WithConfigWriter(configWriter ConfigWriter) CommandFactoryOption {
|
||||
return func(f *CommandFactory) error {
|
||||
f.configWriter = configWriter
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithLoggerFactory sets the logger factory for the command factory.
|
||||
func WithLoggerFactory(loggerFactory func(server.ConfigMap, io.Writer) (log.Logger, error)) CommandFactoryOption {
|
||||
return func(f *CommandFactory) error {
|
||||
f.loggerFactory = loggerFactory
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// enhanceCommand adds the following flags to the command:
|
||||
//
|
||||
// --log-level: The logging level (trace|debug|info|warn|error|fatal|panic|disabled or '*:<level>,<key>:<level>')
|
||||
// --log-format: The logging format (json|plain)
|
||||
// --log-no-color: Disable colored logs
|
||||
// --home: directory for config and data
|
||||
//
|
||||
// It also sets the environment variable prefix for the viper instance.
|
||||
func (f *CommandFactory) enhanceCommand(cmd *cobra.Command) {
|
||||
pflags := cmd.PersistentFlags()
|
||||
pflags.String(FlagLogLevel, "info", "The logging level (trace|debug|info|warn|error|fatal|panic|disabled or '*:<level>,<key>:<level>')")
|
||||
pflags.String(FlagLogFormat, "plain", "The logging format (json|plain)")
|
||||
pflags.Bool(FlagLogNoColor, false, "Disable colored logs")
|
||||
pflags.StringP(FlagHome, "", f.defaultHomeDir, "directory for config and data")
|
||||
viper.SetEnvPrefix(f.envPrefix)
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
|
||||
viper.AutomaticEnv()
|
||||
}
|
||||
|
||||
// EnhanceRootCommand sets the viper and logger in the command context.
|
||||
func (f *CommandFactory) EnhanceRootCommand(cmd *cobra.Command) {
|
||||
f.enhanceCommand(cmd)
|
||||
SetCmdServerContext(cmd, f.vipr, f.logger)
|
||||
}
|
||||
|
||||
// ParseCommand parses args against the input rootCmd CLI skeleton then returns the target subcommand,
|
||||
// a fully realized config map, and a properly configured logger.
|
||||
// If `WithConfigWriter` was set in the factory options, the config writer will be used to write the app.toml file.
|
||||
// Internally a viper instance is created and used to bind the flags to the config map.
|
||||
// Future invocations of EnhanceCommandContext will set the viper instance and logger in the command context.
|
||||
func (f *CommandFactory) ParseCommand(
|
||||
rootCmd *cobra.Command,
|
||||
args []string,
|
||||
) (*cobra.Command, server.ConfigMap, log.Logger, error) {
|
||||
f.enhanceCommand(rootCmd)
|
||||
cmd, _, err := rootCmd.Traverse(args)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if err = cmd.ParseFlags(args); err != nil {
|
||||
// help requested, return the command early
|
||||
if errors.Is(err, pflag.ErrHelp) {
|
||||
return cmd, nil, nil, nil
|
||||
}
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
home, err := cmd.Flags().GetString(FlagHome)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
configDir := filepath.Join(home, "config")
|
||||
if f.configWriter != nil {
|
||||
// create app.toml if it does not already exist
|
||||
if _, err = os.Stat(filepath.Join(configDir, "app.toml")); os.IsNotExist(err) {
|
||||
if err = f.configWriter.WriteConfig(configDir); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
f.vipr, err = ReadConfig(configDir)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
if err = f.vipr.BindPFlags(cmd.Flags()); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
if f.loggerFactory != nil {
|
||||
f.logger, err = f.loggerFactory(f.vipr.AllSettings(), cmd.OutOrStdout())
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return cmd, f.vipr.AllSettings(), f.logger, nil
|
||||
}
|
||||
+54
-95
@@ -5,68 +5,33 @@ import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime/pprof"
|
||||
"slices"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"cosmossdk.io/core/server"
|
||||
"cosmossdk.io/core/transaction"
|
||||
"cosmossdk.io/log"
|
||||
)
|
||||
|
||||
// Execute executes the root command of an application.
|
||||
// It handles adding core CLI flags, specifically the logging flags.
|
||||
func Execute(rootCmd *cobra.Command, envPrefix, defaultHome string) error {
|
||||
rootCmd.PersistentFlags().String(FlagLogLevel, "info", "The logging level (trace|debug|info|warn|error|fatal|panic|disabled or '*:<level>,<key>:<level>')")
|
||||
rootCmd.PersistentFlags().String(FlagLogFormat, "plain", "The logging format (json|plain)")
|
||||
rootCmd.PersistentFlags().Bool(FlagLogNoColor, false, "Disable colored logs")
|
||||
rootCmd.PersistentFlags().StringP(FlagHome, "", defaultHome, "directory for config and data")
|
||||
|
||||
// update the global viper with the root command's configuration
|
||||
viper.SetEnvPrefix(envPrefix)
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
|
||||
viper.AutomaticEnv()
|
||||
|
||||
return rootCmd.Execute()
|
||||
}
|
||||
|
||||
// AddCommands add the server commands to the root command
|
||||
// It configures the config handling and the logger handling
|
||||
func AddCommands[T transaction.Tx](
|
||||
rootCmd *cobra.Command,
|
||||
newApp AppCreator[T],
|
||||
globalServerCfg ServerConfig,
|
||||
logger log.Logger,
|
||||
globalAppConfig server.ConfigMap,
|
||||
globalServerConfig ServerConfig,
|
||||
components ...ServerComponent[T],
|
||||
) error {
|
||||
) (ConfigWriter, error) {
|
||||
if len(components) == 0 {
|
||||
return errors.New("no components provided")
|
||||
return nil, errors.New("no components provided")
|
||||
}
|
||||
|
||||
server := NewServer(globalServerCfg, components...)
|
||||
originalPersistentPreRunE := rootCmd.PersistentPreRunE
|
||||
rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
|
||||
// set the default command outputs
|
||||
cmd.SetOut(cmd.OutOrStdout())
|
||||
cmd.SetErr(cmd.ErrOrStderr())
|
||||
|
||||
if err := configHandle(server, cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// call the original PersistentPreRun(E) if it exists
|
||||
if rootCmd.PersistentPreRun != nil {
|
||||
rootCmd.PersistentPreRun(cmd, args)
|
||||
return nil
|
||||
}
|
||||
|
||||
return originalPersistentPreRunE(cmd, args)
|
||||
}
|
||||
|
||||
cmds := server.CLICommands()
|
||||
startCmd := createStartCommand(server, newApp)
|
||||
srv := NewServer(globalServerConfig, components...)
|
||||
cmds := srv.CLICommands()
|
||||
startCmd := createStartCommand(srv, globalAppConfig, logger)
|
||||
startCmd.SetContext(rootCmd.Context())
|
||||
cmds.Commands = append(cmds.Commands, startCmd)
|
||||
rootCmd.AddCommand(cmds.Commands...)
|
||||
@@ -92,13 +57,14 @@ func AddCommands[T transaction.Tx](
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return srv, nil
|
||||
}
|
||||
|
||||
// createStartCommand creates the start command for the application.
|
||||
func createStartCommand[T transaction.Tx](
|
||||
server *Server[T],
|
||||
newApp AppCreator[T],
|
||||
config server.ConfigMap,
|
||||
logger log.Logger,
|
||||
) *cobra.Command {
|
||||
flags := server.StartFlags()
|
||||
|
||||
@@ -106,16 +72,6 @@ func createStartCommand[T transaction.Tx](
|
||||
Use: "start",
|
||||
Short: "Run the application",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
v := GetViperFromCmd(cmd)
|
||||
l := GetLoggerFromCmd(cmd)
|
||||
if err := v.BindPFlags(cmd.Flags()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := server.Init(newApp(l, v), v.AllSettings(), l); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancelFn := context.WithCancel(cmd.Context())
|
||||
go func() {
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
@@ -135,7 +91,7 @@ func createStartCommand[T transaction.Tx](
|
||||
}
|
||||
}()
|
||||
|
||||
return wrapCPUProfile(l, v, func() error {
|
||||
return wrapCPUProfile(logger, config, func() error {
|
||||
return server.Start(ctx)
|
||||
})
|
||||
},
|
||||
@@ -151,14 +107,14 @@ func createStartCommand[T transaction.Tx](
|
||||
|
||||
// wrapCPUProfile starts CPU profiling, if enabled, and executes the provided
|
||||
// callbackFn, then waits for it to return.
|
||||
func wrapCPUProfile(logger log.Logger, v *viper.Viper, callbackFn func() error) error {
|
||||
cpuProfileFile := v.GetString(FlagCPUProfiling)
|
||||
if len(cpuProfileFile) == 0 {
|
||||
func wrapCPUProfile(logger log.Logger, cfg server.ConfigMap, callbackFn func() error) error {
|
||||
cpuProfileFile, ok := cfg[FlagCPUProfiling]
|
||||
if !ok {
|
||||
// if cpu profiling is not enabled, just run the callback
|
||||
return callbackFn()
|
||||
}
|
||||
|
||||
f, err := os.Create(cpuProfileFile)
|
||||
f, err := os.Create(cpuProfileFile.(string))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -180,39 +136,6 @@ func wrapCPUProfile(logger log.Logger, v *viper.Viper, callbackFn func() error)
|
||||
return callbackFn()
|
||||
}
|
||||
|
||||
// configHandle writes the default config to the home directory if it does not exist and sets the server context
|
||||
func configHandle[T transaction.Tx](s *Server[T], cmd *cobra.Command) error {
|
||||
home, err := cmd.Flags().GetString(FlagHome)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configDir := filepath.Join(home, "config")
|
||||
|
||||
// we need to check app.toml as the config folder can already exist for the client.toml
|
||||
if _, err := os.Stat(filepath.Join(configDir, "app.toml")); os.IsNotExist(err) {
|
||||
if err = s.WriteConfig(configDir); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
v, err := ReadConfig(configDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := v.BindPFlags(cmd.Flags()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger, err := NewLogger(v, cmd.OutOrStdout())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return SetCmdServerContext(cmd, v, logger)
|
||||
}
|
||||
|
||||
// findSubCommand finds a sub-command of the provided command whose Use
|
||||
// string is or begins with the provided subCmdName.
|
||||
// It verifies the command's aliases as well.
|
||||
@@ -246,3 +169,39 @@ func topLevelCmd(ctx context.Context, use, short string) *cobra.Command {
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// appBuildingCommands are the commands which need a full application to be built
|
||||
var appBuildingCommands = [][]string{
|
||||
{"start"},
|
||||
{"genesis", "export"},
|
||||
}
|
||||
|
||||
// IsAppRequired determines if a command requires a full application to be built by
|
||||
// recursively checking the command hierarchy against known command paths.
|
||||
//
|
||||
// The function works by:
|
||||
// 1. Combining default appBuildingCommands with additional required commands
|
||||
// 2. Building command paths by traversing up the command tree
|
||||
// 3. Checking if any known command path matches the current command path
|
||||
//
|
||||
// Time Complexity: O(d * p) where d is command depth and p is number of paths
|
||||
// Space Complexity: O(p) where p is total number of command paths
|
||||
func IsAppRequired(cmd *cobra.Command, required ...[]string) bool {
|
||||
m := make(map[string]bool)
|
||||
cmds := append(appBuildingCommands, required...)
|
||||
for _, c := range cmds {
|
||||
slices.Reverse(c)
|
||||
m[strings.Join(c, "")] = true
|
||||
}
|
||||
cmdPath := make([]string, 0, 5) // Pre-allocate with reasonable capacity
|
||||
for {
|
||||
cmdPath = append(cmdPath, cmd.Use)
|
||||
if _, ok := m[strings.Join(cmdPath, "")]; ok {
|
||||
return true
|
||||
}
|
||||
if cmd.Parent() == nil {
|
||||
return false
|
||||
}
|
||||
cmd = cmd.Parent()
|
||||
}
|
||||
}
|
||||
|
||||
+28
-9
@@ -4,33 +4,52 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"cosmossdk.io/core/server"
|
||||
"cosmossdk.io/log"
|
||||
)
|
||||
|
||||
// NewLogger creates the default SDK logger.
|
||||
// It reads the log level and format from the server context.
|
||||
func NewLogger(v *viper.Viper, out io.Writer) (log.Logger, error) {
|
||||
func NewLogger(cfg server.ConfigMap, out io.Writer) (log.Logger, error) {
|
||||
var opts []log.Option
|
||||
if v.GetString(FlagLogFormat) == OutputFormatJSON {
|
||||
var (
|
||||
format string
|
||||
noColor bool
|
||||
trace bool
|
||||
level string
|
||||
)
|
||||
if v, ok := cfg[FlagLogFormat]; ok {
|
||||
format = v.(string)
|
||||
}
|
||||
if v, ok := cfg[FlagLogNoColor]; ok {
|
||||
noColor = v.(bool)
|
||||
}
|
||||
if v, ok := cfg[FlagTrace]; ok {
|
||||
trace = v.(bool)
|
||||
}
|
||||
if v, ok := cfg[FlagLogLevel]; ok {
|
||||
level = v.(string)
|
||||
}
|
||||
|
||||
if format == OutputFormatJSON {
|
||||
opts = append(opts, log.OutputJSONOption())
|
||||
}
|
||||
opts = append(opts,
|
||||
log.ColorOption(!v.GetBool(FlagLogNoColor)),
|
||||
log.TraceOption(v.GetBool(FlagTrace)))
|
||||
log.ColorOption(!noColor),
|
||||
log.TraceOption(trace),
|
||||
)
|
||||
|
||||
// check and set filter level or keys for the logger if any
|
||||
logLvlStr := v.GetString(FlagLogLevel)
|
||||
if logLvlStr == "" {
|
||||
if level == "" {
|
||||
return log.NewLogger(out, opts...), nil
|
||||
}
|
||||
|
||||
logLvl, err := zerolog.ParseLevel(logLvlStr)
|
||||
logLvl, err := zerolog.ParseLevel(level)
|
||||
switch {
|
||||
case err != nil:
|
||||
// If the log level is not a valid zerolog level, then we try to parse it as a key filter.
|
||||
filterFunc, err := log.ParseLogLevel(logLvlStr)
|
||||
filterFunc, err := log.ParseLogLevel(level)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+5
-25
@@ -22,7 +22,6 @@ type ServerComponent[T transaction.Tx] interface {
|
||||
|
||||
Start(context.Context) error
|
||||
Stop(context.Context) error
|
||||
Init(AppI[T], map[string]any, log.Logger) error
|
||||
}
|
||||
|
||||
// HasStartFlags is a server module that has start flags.
|
||||
@@ -38,6 +37,11 @@ type HasConfig interface {
|
||||
Config() any
|
||||
}
|
||||
|
||||
// ConfigWriter is a server module that can write its config to a file.
|
||||
type ConfigWriter interface {
|
||||
WriteConfig(path string) error
|
||||
}
|
||||
|
||||
// HasCLICommands is a server module that has CLI commands.
|
||||
type HasCLICommands interface {
|
||||
CLICommands() CLIConfig
|
||||
@@ -186,30 +190,6 @@ func (s *Server[T]) StartCmdFlags() *pflag.FlagSet {
|
||||
return flags
|
||||
}
|
||||
|
||||
// Init initializes all server components with the provided application, configuration, and logger.
|
||||
// It returns an error if any component fails to initialize.
|
||||
func (s *Server[T]) Init(appI AppI[T], cfg map[string]any, logger log.Logger) error {
|
||||
serverCfg := s.config
|
||||
if len(cfg) > 0 {
|
||||
if err := UnmarshalSubConfig(cfg, s.Name(), &serverCfg); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
var components []ServerComponent[T]
|
||||
for _, mod := range s.components {
|
||||
if err := mod.Init(appI, cfg, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
components = append(components, mod)
|
||||
}
|
||||
|
||||
s.config = serverCfg
|
||||
s.components = components
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteConfig writes the config to the given path.
|
||||
// Note: it does not use viper.WriteConfigAs because we do not want to store flag values in the config.
|
||||
func (s *Server[T]) WriteConfig(configPath string) error {
|
||||
|
||||
@@ -4,10 +4,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"cosmossdk.io/core/transaction"
|
||||
"cosmossdk.io/log"
|
||||
serverv2 "cosmossdk.io/server/v2"
|
||||
)
|
||||
|
||||
type mockServerConfig struct {
|
||||
@@ -31,10 +27,6 @@ func (s *mockServer) Name() string {
|
||||
return s.name
|
||||
}
|
||||
|
||||
func (s *mockServer) Init(appI serverv2.AppI[transaction.Tx], cfg map[string]any, logger log.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *mockServer) Start(ctx context.Context) error {
|
||||
for ctx.Err() == nil {
|
||||
s.ch <- fmt.Sprintf("%s mock server: %d", s.name, rand.Int())
|
||||
|
||||
@@ -61,20 +61,18 @@ func TestServer(t *testing.T) {
|
||||
|
||||
logger := log.NewLogger(os.Stdout)
|
||||
|
||||
ctx, err := serverv2.SetServerContext(context.Background(), v, logger)
|
||||
ctx := serverv2.SetServerContext(context.Background(), v, logger)
|
||||
app := &mockApp[transaction.Tx]{}
|
||||
|
||||
grpcServer, err := grpc.New[transaction.Tx](logger, app.InterfaceRegistry(), app.QueryHandlers(), app, cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
grpcServer := grpc.New[transaction.Tx]()
|
||||
err = grpcServer.Init(&mockApp[transaction.Tx]{}, cfg, logger)
|
||||
require.NoError(t, err)
|
||||
|
||||
storeServer := store.New[transaction.Tx]()
|
||||
err = storeServer.Init(&mockApp[transaction.Tx]{}, cfg, logger)
|
||||
storeServer, err := store.New[transaction.Tx](app.Store(), cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
mockServer := &mockServer{name: "mock-server-1", ch: make(chan string, 100)}
|
||||
|
||||
server := serverv2.NewServer(
|
||||
server := serverv2.NewServer[transaction.Tx](
|
||||
serverv2.DefaultServerConfig(),
|
||||
grpcServer,
|
||||
storeServer,
|
||||
|
||||
+11
-10
@@ -6,8 +6,8 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"cosmossdk.io/core/server"
|
||||
"cosmossdk.io/core/transaction"
|
||||
"cosmossdk.io/log"
|
||||
serverv2 "cosmossdk.io/server/v2"
|
||||
storev2 "cosmossdk.io/store/v2"
|
||||
"cosmossdk.io/store/v2/root"
|
||||
@@ -27,14 +27,15 @@ type Server[T transaction.Tx] struct {
|
||||
backend storev2.Backend
|
||||
}
|
||||
|
||||
func New[T transaction.Tx]() *Server[T] {
|
||||
return &Server[T]{}
|
||||
}
|
||||
|
||||
func (s *Server[T]) Init(app serverv2.AppI[T], v map[string]any, _ log.Logger) (err error) {
|
||||
s.backend = app.Store()
|
||||
s.config, err = UnmarshalConfig(v)
|
||||
return err
|
||||
func New[T transaction.Tx](store storev2.Backend, cfg server.ConfigMap) (*Server[T], error) {
|
||||
config, err := UnmarshalConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Server[T]{
|
||||
backend: store,
|
||||
config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server[T]) Name() string {
|
||||
@@ -58,7 +59,7 @@ func (s *Server[T]) CLICommands() serverv2.CLIConfig {
|
||||
s.ListSnapshotsCmd(),
|
||||
s.DumpArchiveCmd(),
|
||||
s.LoadArchiveCmd(),
|
||||
s.RestoreSnapshotCmd(s.backend),
|
||||
s.RestoreSnapshotCmd(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ func (s *Server[T]) ExportSnapshotCmd() *cobra.Command {
|
||||
}
|
||||
|
||||
// RestoreSnapshotCmd returns a command to restore a snapshot
|
||||
func (s *Server[T]) RestoreSnapshotCmd(rootStore storev2.Backend) *cobra.Command {
|
||||
func (s *Server[T]) RestoreSnapshotCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "restore <height> <format>",
|
||||
Short: "Restore app state from local snapshot",
|
||||
@@ -95,6 +95,10 @@ func (s *Server[T]) RestoreSnapshotCmd(rootStore storev2.Backend) *cobra.Command
|
||||
|
||||
logger := log.NewLogger(cmd.OutOrStdout())
|
||||
|
||||
rootStore, _, err := createRootStore(v, logger)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create root store: %w", err)
|
||||
}
|
||||
sm, err := createSnapshotsManager(cmd, v, logger, rootStore)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
+4
-8
@@ -15,26 +15,22 @@ import (
|
||||
|
||||
// SetServerContext sets the logger and viper in the context.
|
||||
// The server manager expects the logger and viper to be set in the context.
|
||||
func SetServerContext(ctx context.Context, viper *viper.Viper, logger log.Logger) (context.Context, error) {
|
||||
func SetServerContext(ctx context.Context, viper *viper.Viper, logger log.Logger) context.Context {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, corectx.LoggerContextKey, logger)
|
||||
ctx = context.WithValue(ctx, corectx.ViperContextKey, viper)
|
||||
return ctx, nil
|
||||
return ctx
|
||||
}
|
||||
|
||||
// SetCmdServerContext sets a command's Context value to the provided argument.
|
||||
// The server manager expects the logger and viper to be set in the context.
|
||||
// If the context has not been set, set the given context as the default.
|
||||
func SetCmdServerContext(cmd *cobra.Command, viper *viper.Viper, logger log.Logger) error {
|
||||
ctx, err := SetServerContext(cmd.Context(), viper, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func SetCmdServerContext(cmd *cobra.Command, viper *viper.Viper, logger log.Logger) {
|
||||
ctx := SetServerContext(cmd.Context(), viper, logger)
|
||||
cmd.SetContext(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetViperFromContext returns the viper instance from the context.
|
||||
|
||||
+4
-2
@@ -93,8 +93,10 @@ func init() {
|
||||
// AppConfig returns the default app config.
|
||||
func AppConfig() depinject.Config {
|
||||
return depinject.Configs(
|
||||
appConfig, // Alternatively use appconfig.LoadYAML(AppConfigYAML)
|
||||
depinject.Provide(ProvideExampleMintFn), // optional: override the mint module's mint function with epoched minting
|
||||
appConfig, // Alternatively use appconfig.LoadYAML(AppConfigYAML)
|
||||
depinject.Provide(
|
||||
ProvideExampleMintFn, // optional: override the mint module's mint function with epoched minting
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -106,8 +106,8 @@ var (
|
||||
// pooltypes.ModuleName
|
||||
}
|
||||
|
||||
// application configuration (used by depinject)
|
||||
appConfig = appconfig.Compose(&appv1alpha1.Config{
|
||||
// ModuleConfig is the application module configuration used by depinject
|
||||
ModuleConfig = appconfig.Compose(&appv1alpha1.Config{
|
||||
Modules: []*appv1alpha1.ModuleConfig{
|
||||
{
|
||||
Name: runtime.ModuleName,
|
||||
|
||||
+81
-99
@@ -2,10 +2,8 @@ package simapp
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
clienthelpers "cosmossdk.io/client/v2/helpers"
|
||||
"cosmossdk.io/core/registry"
|
||||
"cosmossdk.io/core/server"
|
||||
"cosmossdk.io/core/transaction"
|
||||
@@ -28,9 +26,6 @@ import (
|
||||
_ "github.com/cosmos/cosmos-sdk/x/genutil"
|
||||
)
|
||||
|
||||
// DefaultNodeHome default home directories for the application daemon
|
||||
var DefaultNodeHome string
|
||||
|
||||
// SimApp extends an ABCI application, but with most of its parameters exported.
|
||||
// They are exported for convenience in creating helper functions, as object
|
||||
// capabilities aren't needed for testing.
|
||||
@@ -48,24 +43,24 @@ type SimApp[T transaction.Tx] struct {
|
||||
StakingKeeper *stakingkeeper.Keeper
|
||||
}
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
DefaultNodeHome, err = clienthelpers.GetNodeHomeDirectory(".simappv2")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// AppConfig returns the default app config.
|
||||
func AppConfig() depinject.Config {
|
||||
return depinject.Configs(
|
||||
appConfig, // Alternatively use appconfig.LoadYAML(AppConfigYAML)
|
||||
ModuleConfig, // Alternatively use appconfig.LoadYAML(AppConfigYAML)
|
||||
runtime.DefaultServiceBindings(),
|
||||
depinject.Provide(
|
||||
codec.ProvideInterfaceRegistry,
|
||||
codec.ProvideAddressCodec,
|
||||
codec.ProvideProtoCodec,
|
||||
codec.ProvideLegacyAmino,
|
||||
ProvideRootStoreConfig,
|
||||
// inject desired account types:
|
||||
multisigdepinject.ProvideAccount,
|
||||
basedepinject.ProvideAccount,
|
||||
lockupdepinject.ProvideAllLockupAccounts,
|
||||
|
||||
// provide base account options
|
||||
basedepinject.ProvideSecp256K1PubKey,
|
||||
),
|
||||
depinject.Invoke(
|
||||
std.RegisterInterfaces,
|
||||
@@ -74,89 +69,80 @@ func AppConfig() depinject.Config {
|
||||
)
|
||||
}
|
||||
|
||||
// NewSimApp returns a reference to an initialized SimApp.
|
||||
func NewSimApp[T transaction.Tx](
|
||||
logger log.Logger,
|
||||
viper *viper.Viper,
|
||||
) *SimApp[T] {
|
||||
config depinject.Config,
|
||||
outputs ...any,
|
||||
) (*SimApp[T], error) {
|
||||
var (
|
||||
app = &SimApp[T]{}
|
||||
appBuilder *runtime.AppBuilder[T]
|
||||
err error
|
||||
storeBuilder root.Builder
|
||||
logger log.Logger
|
||||
|
||||
// merge the AppConfig and other configuration in one config
|
||||
appConfig = depinject.Configs(
|
||||
AppConfig(),
|
||||
config,
|
||||
depinject.Supply(
|
||||
logger,
|
||||
viper,
|
||||
// ADVANCED CONFIGURATION
|
||||
|
||||
// ADVANCED CONFIGURATION
|
||||
//
|
||||
// AUTH
|
||||
//
|
||||
// For providing a custom function required in auth to generate custom account types
|
||||
// add it below. By default the auth module uses simulation.RandomGenesisAccounts.
|
||||
//
|
||||
// authtypes.RandomGenesisAccountsFn(simulation.RandomGenesisAccounts),
|
||||
//
|
||||
// For providing a custom a base account type add it below.
|
||||
// By default the auth module uses authtypes.ProtoBaseAccount().
|
||||
//
|
||||
// func() sdk.AccountI { return authtypes.ProtoBaseAccount() },
|
||||
//
|
||||
// For providing a different address codec, add it below.
|
||||
// By default the auth module uses a Bech32 address codec,
|
||||
// with the prefix defined in the auth module configuration.
|
||||
//
|
||||
// func() address.Codec { return <- custom address codec type -> }
|
||||
|
||||
//
|
||||
// AUTH
|
||||
//
|
||||
// For providing a custom function required in auth to generate custom account types
|
||||
// add it below. By default the auth module uses simulation.RandomGenesisAccounts.
|
||||
//
|
||||
// authtypes.RandomGenesisAccountsFn(simulation.RandomGenesisAccounts),
|
||||
//
|
||||
// For providing a custom a base account type add it below.
|
||||
// By default the auth module uses authtypes.ProtoBaseAccount().
|
||||
//
|
||||
// func() sdk.AccountI { return authtypes.ProtoBaseAccount() },
|
||||
//
|
||||
// For providing a different address codec, add it below.
|
||||
// By default the auth module uses a Bech32 address codec,
|
||||
// with the prefix defined in the auth module configuration.
|
||||
//
|
||||
// func() address.Codec { return <- custom address codec type -> }
|
||||
//
|
||||
// STAKING
|
||||
//
|
||||
// For provinding a different validator and consensus address codec, add it below.
|
||||
// By default the staking module uses the bech32 prefix provided in the auth config,
|
||||
// and appends "valoper" and "valcons" for validator and consensus addresses respectively.
|
||||
// When providing a custom address codec in auth, custom address codecs must be provided here as well.
|
||||
//
|
||||
// func() runtime.ValidatorAddressCodec { return <- custom validator address codec type -> }
|
||||
// func() runtime.ConsensusAddressCodec { return <- custom consensus address codec type -> }
|
||||
|
||||
//
|
||||
// STAKING
|
||||
//
|
||||
// For provinding a different validator and consensus address codec, add it below.
|
||||
// By default the staking module uses the bech32 prefix provided in the auth config,
|
||||
// and appends "valoper" and "valcons" for validator and consensus addresses respectively.
|
||||
// When providing a custom address codec in auth, custom address codecs must be provided here as well.
|
||||
//
|
||||
// func() runtime.ValidatorAddressCodec { return <- custom validator address codec type -> }
|
||||
// func() runtime.ConsensusAddressCodec { return <- custom consensus address codec type -> }
|
||||
//
|
||||
// MINT
|
||||
//
|
||||
|
||||
//
|
||||
// MINT
|
||||
//
|
||||
|
||||
// For providing a custom inflation function for x/mint add here your
|
||||
// custom function that implements the minttypes.InflationCalculationFn
|
||||
// interface.
|
||||
// For providing a custom inflation function for x/mint add here your
|
||||
// custom function that implements the minttypes.InflationCalculationFn
|
||||
// interface.
|
||||
),
|
||||
depinject.Provide(
|
||||
// inject desired account types:
|
||||
multisigdepinject.ProvideAccount,
|
||||
basedepinject.ProvideAccount,
|
||||
lockupdepinject.ProvideAllLockupAccounts,
|
||||
|
||||
// provide base account options
|
||||
basedepinject.ProvideSecp256K1PubKey,
|
||||
// if you want to provide a custom public key you
|
||||
// can do it from here.
|
||||
// Example:
|
||||
// basedepinject.ProvideCustomPubkey[Ed25519PublicKey]()
|
||||
//
|
||||
// You can also provide a custom public key with a custom validation function:
|
||||
//
|
||||
// basedepinject.ProvideCustomPubKeyAndValidationFunc(func(pub Ed25519PublicKey) error {
|
||||
// if len(pub.Key) != 64 {
|
||||
// return fmt.Errorf("invalid pub key size")
|
||||
// }
|
||||
// })
|
||||
// if you want to provide a custom public key you
|
||||
// can do it from here.
|
||||
// Example:
|
||||
// basedepinject.ProvideCustomPubkey[Ed25519PublicKey]()
|
||||
//
|
||||
// You can also provide a custom public key with a custom validation function:
|
||||
//
|
||||
// basedepinject.ProvideCustomPubKeyAndValidationFunc(func(pub Ed25519PublicKey) error {
|
||||
// if len(pub.Key) != 64 {
|
||||
// return fmt.Errorf("invalid pub key size")
|
||||
// }
|
||||
// })
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
if err := depinject.Inject(appConfig,
|
||||
outputs = append(outputs,
|
||||
&logger,
|
||||
&storeBuilder,
|
||||
&appBuilder,
|
||||
&app.appCodec,
|
||||
@@ -164,25 +150,21 @@ func NewSimApp[T transaction.Tx](
|
||||
&app.txConfig,
|
||||
&app.interfaceRegistry,
|
||||
&app.UpgradeKeeper,
|
||||
&app.StakingKeeper,
|
||||
); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
&app.StakingKeeper)
|
||||
|
||||
// store/v2 follows a slightly more eager config life cycle than server components
|
||||
storeConfig, err := serverstore.UnmarshalConfig(viper.AllSettings())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
app.store, err = storeBuilder.Build(logger, storeConfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
if err := depinject.Inject(appConfig, outputs...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var err error
|
||||
app.App, err = appBuilder.Build()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
app.store = storeBuilder.Get()
|
||||
if app.store == nil {
|
||||
return nil, fmt.Errorf("store builder did not return a db")
|
||||
}
|
||||
|
||||
/**** Module Options ****/
|
||||
@@ -190,14 +172,10 @@ func NewSimApp[T transaction.Tx](
|
||||
// RegisterUpgradeHandlers is used for registering any on-chain upgrades.
|
||||
app.RegisterUpgradeHandlers()
|
||||
|
||||
// TODO (here or in runtime/v2)
|
||||
// wire simulation manager
|
||||
// wire unordered tx manager
|
||||
|
||||
if err := app.LoadLatest(); err != nil {
|
||||
panic(err)
|
||||
if err = app.LoadLatest(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return app
|
||||
return app, nil
|
||||
}
|
||||
|
||||
// AppCodec returns SimApp's app codec.
|
||||
@@ -231,3 +209,7 @@ func (app *SimApp[T]) Close() error {
|
||||
|
||||
return app.App.Close()
|
||||
}
|
||||
|
||||
func ProvideRootStoreConfig(config runtime.GlobalConfig) (*root.Config, error) {
|
||||
return serverstore.UnmarshalConfig(config)
|
||||
}
|
||||
|
||||
@@ -16,8 +16,10 @@ import (
|
||||
"cosmossdk.io/core/server"
|
||||
"cosmossdk.io/core/store"
|
||||
"cosmossdk.io/core/transaction"
|
||||
"cosmossdk.io/depinject"
|
||||
"cosmossdk.io/log"
|
||||
sdkmath "cosmossdk.io/math"
|
||||
"cosmossdk.io/runtime/v2"
|
||||
serverv2 "cosmossdk.io/server/v2"
|
||||
serverv2store "cosmossdk.io/server/v2/store"
|
||||
"cosmossdk.io/store/v2/db"
|
||||
@@ -39,7 +41,11 @@ func NewTestApp(t *testing.T) (*SimApp[transaction.Tx], context.Context) {
|
||||
vp.Set(serverv2store.FlagAppDBBackend, string(db.DBTypeGoLevelDB))
|
||||
vp.Set(serverv2.FlagHome, t.TempDir())
|
||||
|
||||
app := NewSimApp[transaction.Tx](logger, vp)
|
||||
app, err := NewSimApp[transaction.Tx](depinject.Configs(
|
||||
depinject.Supply(logger, runtime.GlobalConfig(vp.AllSettings()))),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
genesis := app.ModuleManager().DefaultGenesis()
|
||||
|
||||
privVal := mock.NewPV()
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"cosmossdk.io/client/v2/offchain"
|
||||
corectx "cosmossdk.io/core/context"
|
||||
coreserver "cosmossdk.io/core/server"
|
||||
"cosmossdk.io/core/transaction"
|
||||
"cosmossdk.io/log"
|
||||
runtimev2 "cosmossdk.io/runtime/v2"
|
||||
@@ -17,82 +14,140 @@ import (
|
||||
"cosmossdk.io/server/v2/api/grpc"
|
||||
"cosmossdk.io/server/v2/api/rest"
|
||||
"cosmossdk.io/server/v2/api/telemetry"
|
||||
"cosmossdk.io/server/v2/cometbft"
|
||||
serverstore "cosmossdk.io/server/v2/store"
|
||||
"cosmossdk.io/simapp/v2"
|
||||
confixcmd "cosmossdk.io/tools/confix/cmd"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/config"
|
||||
"github.com/cosmos/cosmos-sdk/client/debug"
|
||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
||||
"github.com/cosmos/cosmos-sdk/client/rpc"
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
|
||||
"github.com/cosmos/cosmos-sdk/x/genutil"
|
||||
genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
|
||||
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
|
||||
genutilv2 "github.com/cosmos/cosmos-sdk/x/genutil/v2"
|
||||
v2 "github.com/cosmos/cosmos-sdk/x/genutil/v2/cli"
|
||||
)
|
||||
|
||||
func newApp[T transaction.Tx](logger log.Logger, viper *viper.Viper) serverv2.AppI[T] {
|
||||
viper.SetDefault(serverv2.FlagHome, simapp.DefaultNodeHome)
|
||||
return serverv2.AppI[T](simapp.NewSimApp[T](logger, viper))
|
||||
// CommandDependencies is a struct that contains all the dependencies needed to initialize the root command.
|
||||
// an alternative design could fetch these even later from the command context
|
||||
type CommandDependencies[T transaction.Tx] struct {
|
||||
GlobalConfig coreserver.ConfigMap
|
||||
TxConfig client.TxConfig
|
||||
ModuleManager *runtimev2.MM[T]
|
||||
SimApp *simapp.SimApp[T]
|
||||
Consensus serverv2.ServerComponent[T]
|
||||
}
|
||||
|
||||
func initRootCmd[T transaction.Tx](
|
||||
func InitRootCmd[T transaction.Tx](
|
||||
rootCmd *cobra.Command,
|
||||
moduleManager *runtimev2.MM[T],
|
||||
consensusComponent serverv2.ServerComponent[T],
|
||||
) {
|
||||
logger log.Logger,
|
||||
deps CommandDependencies[T],
|
||||
) (serverv2.ConfigWriter, error) {
|
||||
cfg := sdk.GetConfig()
|
||||
cfg.Seal()
|
||||
|
||||
rootCmd.AddCommand(
|
||||
genutilcli.InitCmd(moduleManager),
|
||||
genutilcli.InitCmd(deps.ModuleManager),
|
||||
genesisCommand(deps.ModuleManager, deps.SimApp),
|
||||
NewTestnetCmd(deps.ModuleManager),
|
||||
debug.Cmd(),
|
||||
confixcmd.ConfigCommand(),
|
||||
NewTestnetCmd(moduleManager),
|
||||
)
|
||||
|
||||
// add keybase, auxiliary RPC, query, genesis, and tx child commands
|
||||
rootCmd.AddCommand(
|
||||
genesisCommand(moduleManager),
|
||||
// add keybase, auxiliary RPC, query, genesis, and tx child commands
|
||||
queryCommand(),
|
||||
txCommand(),
|
||||
keys.Commands(),
|
||||
offchain.OffChain(),
|
||||
)
|
||||
|
||||
// wire server commands
|
||||
if err := serverv2.AddCommands(
|
||||
rootCmd,
|
||||
newApp,
|
||||
initServerConfig(),
|
||||
consensusComponent,
|
||||
grpc.New[T](),
|
||||
serverstore.New[T](),
|
||||
telemetry.New[T](),
|
||||
rest.New[T](),
|
||||
); err != nil {
|
||||
panic(err)
|
||||
// build CLI skeleton for initial config parsing or a client application invocation
|
||||
if deps.SimApp == nil {
|
||||
if deps.Consensus == nil {
|
||||
deps.Consensus = cometbft.NewWithConfigOptions[T](initCometConfig())
|
||||
}
|
||||
return serverv2.AddCommands[T](
|
||||
rootCmd,
|
||||
logger,
|
||||
deps.GlobalConfig,
|
||||
initServerConfig(),
|
||||
deps.Consensus,
|
||||
&grpc.Server[T]{},
|
||||
&serverstore.Server[T]{},
|
||||
&telemetry.Server[T]{},
|
||||
&rest.Server[T]{},
|
||||
)
|
||||
}
|
||||
|
||||
// build full app!
|
||||
simApp := deps.SimApp
|
||||
grpcServer, err := grpc.New[T](logger, simApp.InterfaceRegistry(), simApp.QueryHandlers(), simApp, deps.GlobalConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// store component (not a server)
|
||||
storeComponent, err := serverstore.New[T](simApp.Store(), deps.GlobalConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
restServer, err := rest.New[T](simApp.App.AppManager, logger, deps.GlobalConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// consensus component
|
||||
if deps.Consensus == nil {
|
||||
deps.Consensus, err = cometbft.New(
|
||||
logger,
|
||||
simApp.Name(),
|
||||
simApp.Store(),
|
||||
simApp.App.AppManager,
|
||||
simApp.App.QueryHandlers(),
|
||||
simApp.App.SchemaDecoderResolver(),
|
||||
&genericTxDecoder[T]{deps.TxConfig},
|
||||
deps.GlobalConfig,
|
||||
initCometOptions[T](),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
telemetryServer, err := telemetry.New[T](deps.GlobalConfig, logger)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// wire server commands
|
||||
return serverv2.AddCommands[T](
|
||||
rootCmd,
|
||||
logger,
|
||||
deps.GlobalConfig,
|
||||
initServerConfig(),
|
||||
deps.Consensus,
|
||||
grpcServer,
|
||||
storeComponent,
|
||||
telemetryServer,
|
||||
restServer,
|
||||
)
|
||||
}
|
||||
|
||||
// genesisCommand builds genesis-related `simd genesis` command.
|
||||
func genesisCommand[T transaction.Tx](
|
||||
moduleManager *runtimev2.MM[T],
|
||||
cmds ...*cobra.Command,
|
||||
app *simapp.SimApp[T],
|
||||
) *cobra.Command {
|
||||
var genTxValidator func([]transaction.Msg) error
|
||||
if moduleManager != nil {
|
||||
genTxValidator = moduleManager.Modules()[genutiltypes.ModuleName].(genutil.AppModule).GenTxValidator()
|
||||
}
|
||||
cmd := v2.Commands(
|
||||
moduleManager.Modules()[genutiltypes.ModuleName].(genutil.AppModule),
|
||||
genTxValidator,
|
||||
moduleManager,
|
||||
appExport[T],
|
||||
app,
|
||||
)
|
||||
|
||||
for _, subCmd := range cmds {
|
||||
cmd.AddCommand(subCmd)
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -139,41 +194,31 @@ func txCommand() *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
// appExport creates a new simapp (optionally at a given height) and exports state.
|
||||
func appExport[T transaction.Tx](
|
||||
ctx context.Context,
|
||||
height int64,
|
||||
jailAllowedAddrs []string,
|
||||
) (genutilv2.ExportedApp, error) {
|
||||
value := ctx.Value(corectx.ViperContextKey)
|
||||
viper, ok := value.(*viper.Viper)
|
||||
if !ok {
|
||||
return genutilv2.ExportedApp{},
|
||||
fmt.Errorf("incorrect viper type %T: expected *viper.Viper in context", value)
|
||||
}
|
||||
value = ctx.Value(corectx.LoggerContextKey)
|
||||
logger, ok := value.(log.Logger)
|
||||
if !ok {
|
||||
return genutilv2.ExportedApp{},
|
||||
fmt.Errorf("incorrect logger type %T: expected log.Logger in context", value)
|
||||
}
|
||||
func RootCommandPersistentPreRun(clientCtx client.Context) func(*cobra.Command, []string) error {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
// set the default command outputs
|
||||
cmd.SetOut(cmd.OutOrStdout())
|
||||
cmd.SetErr(cmd.ErrOrStderr())
|
||||
|
||||
// overwrite the FlagInvCheckPeriod
|
||||
viper.Set(server.FlagInvCheckPeriod, 1)
|
||||
viper.SetDefault(serverv2.FlagHome, simapp.DefaultNodeHome)
|
||||
|
||||
var simApp *simapp.SimApp[T]
|
||||
if height != -1 {
|
||||
simApp = simapp.NewSimApp[T](logger, viper)
|
||||
|
||||
if err := simApp.LoadHeight(uint64(height)); err != nil {
|
||||
return genutilv2.ExportedApp{}, err
|
||||
clientCtx = clientCtx.WithCmdContext(cmd.Context())
|
||||
clientCtx, err := client.ReadPersistentCommandFlags(clientCtx, cmd.Flags())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
simApp = simapp.NewSimApp[T](logger, viper)
|
||||
}
|
||||
|
||||
return simApp.ExportAppStateAndValidators(jailAllowedAddrs)
|
||||
customClientTemplate, customClientConfig := initClientConfig()
|
||||
clientCtx, err = config.CreateClientConfig(
|
||||
clientCtx, customClientTemplate, customClientConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = client.SetCmdClientContextHandler(clientCtx, cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var _ transaction.Codec[transaction.Tx] = &genericTxDecoder[transaction.Tx]{}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"cosmossdk.io/core/address"
|
||||
"cosmossdk.io/core/registry"
|
||||
"cosmossdk.io/runtime/v2"
|
||||
serverv2 "cosmossdk.io/server/v2"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/config"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/tx"
|
||||
authtxconfig "github.com/cosmos/cosmos-sdk/x/auth/tx/config"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
)
|
||||
|
||||
// ProvideClientContext is a depinject Provider function which assembles and returns a client.Context.
|
||||
func ProvideClientContext(
|
||||
configMap runtime.GlobalConfig,
|
||||
appCodec codec.Codec,
|
||||
interfaceRegistry codectypes.InterfaceRegistry,
|
||||
txConfigOpts tx.ConfigOptions,
|
||||
legacyAmino registry.AminoRegistrar,
|
||||
addressCodec address.Codec,
|
||||
validatorAddressCodec address.ValidatorAddressCodec,
|
||||
consensusAddressCodec address.ConsensusAddressCodec,
|
||||
) client.Context {
|
||||
var err error
|
||||
amino, ok := legacyAmino.(*codec.LegacyAmino)
|
||||
if !ok {
|
||||
panic("registry.AminoRegistrar must be an *codec.LegacyAmino instance for legacy ClientContext")
|
||||
}
|
||||
homeDir, ok := configMap[serverv2.FlagHome].(string)
|
||||
if !ok {
|
||||
panic("server.ConfigMap must contain a string value for serverv2.FlagHome")
|
||||
}
|
||||
|
||||
clientCtx := client.Context{}.
|
||||
WithCodec(appCodec).
|
||||
WithInterfaceRegistry(interfaceRegistry).
|
||||
WithLegacyAmino(amino).
|
||||
WithInput(os.Stdin).
|
||||
WithAccountRetriever(types.AccountRetriever{}).
|
||||
WithAddressCodec(addressCodec).
|
||||
WithValidatorAddressCodec(validatorAddressCodec).
|
||||
WithConsensusAddressCodec(consensusAddressCodec).
|
||||
WithHomeDir(homeDir).
|
||||
WithViper("") // uses by default the binary name as prefix
|
||||
|
||||
// Read the config to overwrite the default values with the values from the config file
|
||||
customClientTemplate, customClientConfig := initClientConfig()
|
||||
clientCtx, err = config.CreateClientConfig(clientCtx, customClientTemplate, customClientConfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// textual is enabled by default, we need to re-create the tx config grpc instead of bank keeper.
|
||||
txConfigOpts.TextualCoinMetadataQueryFn = authtxconfig.NewGRPCCoinMetadataQueryFn(clientCtx)
|
||||
txConfig, err := tx.NewTxConfigWithOptions(clientCtx.Codec, txConfigOpts)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
clientCtx = clientCtx.WithTxConfig(txConfig)
|
||||
|
||||
return clientCtx
|
||||
}
|
||||
+70
-133
@@ -1,161 +1,98 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
|
||||
"cosmossdk.io/client/v2/autocli"
|
||||
"cosmossdk.io/core/address"
|
||||
"cosmossdk.io/core/registry"
|
||||
"cosmossdk.io/core/transaction"
|
||||
"cosmossdk.io/depinject"
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/runtime/v2"
|
||||
serverv2 "cosmossdk.io/server/v2"
|
||||
"cosmossdk.io/server/v2/cometbft"
|
||||
"cosmossdk.io/simapp/v2"
|
||||
basedepinject "cosmossdk.io/x/accounts/defaults/base/depinject"
|
||||
lockupdepinject "cosmossdk.io/x/accounts/defaults/lockup/depinject"
|
||||
multisigdepinject "cosmossdk.io/x/accounts/defaults/multisig/depinject"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/config"
|
||||
nodeservice "github.com/cosmos/cosmos-sdk/client/grpc/node"
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/tx"
|
||||
authtxconfig "github.com/cosmos/cosmos-sdk/x/auth/tx/config"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
)
|
||||
|
||||
// NewCometBFTRootCmd creates a new root command for simd,
|
||||
// using the CometBFT server component for consensus.
|
||||
// It is called once in the main function.
|
||||
func NewCometBFTRootCmd[T transaction.Tx]() *cobra.Command {
|
||||
return NewRootCmdWithConsensusComponent(func(cc client.Context) serverv2.ServerComponent[T] {
|
||||
return cometbft.New[T](
|
||||
&genericTxDecoder[T]{cc.TxConfig},
|
||||
initCometOptions[T](),
|
||||
initCometConfig(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// NewRootCmdWithConsensusComponent returns a new root command,
|
||||
// using the provided callback to instantiate the server component for the consensus layer.
|
||||
// Callers who want to use CometBFT should call [NewCometBFTRootCmd] directly.
|
||||
func NewRootCmdWithConsensusComponent[T transaction.Tx](
|
||||
makeConsensusComponent func(cc client.Context) serverv2.ServerComponent[T],
|
||||
) *cobra.Command {
|
||||
var (
|
||||
autoCliOpts autocli.AppOptions
|
||||
moduleManager *runtime.MM[T]
|
||||
clientCtx client.Context
|
||||
)
|
||||
|
||||
if err := depinject.Inject(
|
||||
depinject.Configs(
|
||||
simapp.AppConfig(),
|
||||
depinject.Provide(
|
||||
ProvideClientContext,
|
||||
// inject desired account types:
|
||||
multisigdepinject.ProvideAccount,
|
||||
basedepinject.ProvideAccount,
|
||||
lockupdepinject.ProvideAllLockupAccounts,
|
||||
|
||||
// provide base account options
|
||||
basedepinject.ProvideSecp256K1PubKey),
|
||||
depinject.Supply(log.NewNopLogger()),
|
||||
),
|
||||
&autoCliOpts,
|
||||
&moduleManager,
|
||||
&clientCtx,
|
||||
); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
rootCmd := &cobra.Command{
|
||||
Use: "simd",
|
||||
Short: "simulation app",
|
||||
func NewRootCmd[T transaction.Tx](
|
||||
args ...string,
|
||||
) (*cobra.Command, error) {
|
||||
rootCommand := &cobra.Command{
|
||||
Use: "simdv2",
|
||||
SilenceErrors: true,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
|
||||
clientCtx = clientCtx.WithCmdContext(cmd.Context())
|
||||
clientCtx, err := client.ReadPersistentCommandFlags(clientCtx, cmd.Flags())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
customClientTemplate, customClientConfig := initClientConfig()
|
||||
clientCtx, err = config.CreateClientConfig(clientCtx, customClientTemplate, customClientConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := client.SetCmdClientContextHandler(clientCtx, cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
configWriter, err := InitRootCmd(rootCommand, log.NewNopLogger(), CommandDependencies[T]{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
factory, err := serverv2.NewCommandFactory(
|
||||
serverv2.WithConfigWriter(configWriter),
|
||||
serverv2.WithStdDefaultHomeDir(".simappv2"),
|
||||
serverv2.WithLoggerFactory(serverv2.NewLogger),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
consensusComponent := makeConsensusComponent(clientCtx)
|
||||
initRootCmd(rootCmd, moduleManager, consensusComponent)
|
||||
subCommand, configMap, logger, err := factory.ParseCommand(rootCommand, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
autoCliOpts autocli.AppOptions
|
||||
moduleManager *runtime.MM[T]
|
||||
clientCtx client.Context
|
||||
simApp *simapp.SimApp[T]
|
||||
depinjectConfig = depinject.Configs(
|
||||
depinject.Supply(logger, runtime.GlobalConfig(configMap)),
|
||||
depinject.Provide(ProvideClientContext),
|
||||
)
|
||||
)
|
||||
if serverv2.IsAppRequired(subCommand) {
|
||||
// server construction
|
||||
simApp, err = simapp.NewSimApp[T](depinjectConfig, &autoCliOpts, &moduleManager, &clientCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// client construction
|
||||
if err = depinject.Inject(
|
||||
depinject.Configs(
|
||||
simapp.AppConfig(),
|
||||
depinjectConfig,
|
||||
),
|
||||
&autoCliOpts, &moduleManager, &clientCtx,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
commandDeps := CommandDependencies[T]{
|
||||
GlobalConfig: configMap,
|
||||
TxConfig: clientCtx.TxConfig,
|
||||
ModuleManager: moduleManager,
|
||||
SimApp: simApp,
|
||||
}
|
||||
rootCommand = &cobra.Command{
|
||||
Use: "simdv2",
|
||||
Short: "simulation app",
|
||||
SilenceErrors: true,
|
||||
PersistentPreRunE: RootCommandPersistentPreRun(clientCtx),
|
||||
}
|
||||
factory.EnhanceRootCommand(rootCommand)
|
||||
_, err = InitRootCmd(rootCommand, logger, commandDeps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodeCmds := nodeservice.NewNodeCommands()
|
||||
autoCliOpts.ModuleOptions = make(map[string]*autocliv1.ModuleOptions)
|
||||
autoCliOpts.ModuleOptions[nodeCmds.Name()] = nodeCmds.AutoCLIOptions()
|
||||
if err := autoCliOpts.EnhanceRootCommand(rootCmd); err != nil {
|
||||
panic(err)
|
||||
if err := autoCliOpts.EnhanceRootCommand(rootCommand); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rootCmd
|
||||
}
|
||||
|
||||
func ProvideClientContext(
|
||||
appCodec codec.Codec,
|
||||
interfaceRegistry codectypes.InterfaceRegistry,
|
||||
txConfigOpts tx.ConfigOptions,
|
||||
legacyAmino registry.AminoRegistrar,
|
||||
addressCodec address.Codec,
|
||||
validatorAddressCodec address.ValidatorAddressCodec,
|
||||
consensusAddressCodec address.ConsensusAddressCodec,
|
||||
) client.Context {
|
||||
var err error
|
||||
|
||||
amino, ok := legacyAmino.(*codec.LegacyAmino)
|
||||
if !ok {
|
||||
panic("registry.AminoRegistrar must be an *codec.LegacyAmino instance for legacy ClientContext")
|
||||
}
|
||||
|
||||
clientCtx := client.Context{}.
|
||||
WithCodec(appCodec).
|
||||
WithInterfaceRegistry(interfaceRegistry).
|
||||
WithLegacyAmino(amino).
|
||||
WithInput(os.Stdin).
|
||||
WithAccountRetriever(types.AccountRetriever{}).
|
||||
WithAddressCodec(addressCodec).
|
||||
WithValidatorAddressCodec(validatorAddressCodec).
|
||||
WithConsensusAddressCodec(consensusAddressCodec).
|
||||
WithHomeDir(simapp.DefaultNodeHome).
|
||||
WithViper("") // uses by default the binary name as prefix
|
||||
|
||||
// Read the config to overwrite the default values with the values from the config file
|
||||
customClientTemplate, customClientConfig := initClientConfig()
|
||||
clientCtx, err = config.CreateClientConfig(clientCtx, customClientTemplate, customClientConfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// textual is enabled by default, we need to re-create the tx config grpc instead of bank keeper.
|
||||
txConfigOpts.TextualCoinMetadataQueryFn = authtxconfig.NewGRPCCoinMetadataQueryFn(clientCtx)
|
||||
txConfig, err := tx.NewTxConfigWithOptions(clientCtx.Codec, txConfigOpts)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
clientCtx = clientCtx.WithTxConfig(txConfig)
|
||||
|
||||
return clientCtx
|
||||
return rootCommand, nil
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cosmossdk.io/core/transaction"
|
||||
svrcmd "cosmossdk.io/server/v2"
|
||||
"cosmossdk.io/simapp/v2"
|
||||
"cosmossdk.io/simapp/v2/simdv2/cmd"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
@@ -16,27 +14,29 @@ import (
|
||||
)
|
||||
|
||||
func TestInitCmd(t *testing.T) {
|
||||
rootCmd := cmd.NewCometBFTRootCmd[transaction.Tx]()
|
||||
rootCmd.SetArgs([]string{
|
||||
args := []string{
|
||||
"init", // Test the init cmd
|
||||
"simapp-test", // Moniker
|
||||
fmt.Sprintf("--%s=%s", cli.FlagOverwrite, "true"), // Overwrite genesis.json, in case it already exists
|
||||
})
|
||||
|
||||
require.NoError(t, svrcmd.Execute(rootCmd, "", simapp.DefaultNodeHome))
|
||||
}
|
||||
rootCmd, err := cmd.NewRootCmd[transaction.Tx](args...)
|
||||
require.NoError(t, err)
|
||||
rootCmd.SetArgs(args)
|
||||
require.NoError(t, rootCmd.Execute())
|
||||
}
|
||||
|
||||
func TestHomeFlagRegistration(t *testing.T) {
|
||||
homeDir := "/tmp/foo"
|
||||
|
||||
rootCmd := cmd.NewCometBFTRootCmd[transaction.Tx]()
|
||||
rootCmd.SetArgs([]string{
|
||||
args := []string{
|
||||
"query",
|
||||
fmt.Sprintf("--%s", flags.FlagHome),
|
||||
homeDir,
|
||||
})
|
||||
}
|
||||
|
||||
require.NoError(t, svrcmd.Execute(rootCmd, "", simapp.DefaultNodeHome))
|
||||
rootCmd, err := cmd.NewRootCmd[transaction.Tx](args...)
|
||||
require.NoError(t, err)
|
||||
rootCmd.SetArgs(args)
|
||||
require.NoError(t, rootCmd.Execute())
|
||||
|
||||
result, err := rootCmd.Flags().GetString(flags.FlagHome)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -335,15 +335,10 @@ func initTestnetFiles[T transaction.Tx](
|
||||
serverCfg := serverv2.DefaultServerConfig()
|
||||
serverCfg.MinGasPrices = args.minGasPrices
|
||||
|
||||
// Write server config
|
||||
cometServer := cometbft.New[T](
|
||||
&genericTxDecoder[T]{clientCtx.TxConfig},
|
||||
cometbft.ServerOptions[T]{},
|
||||
cometbft.OverwriteDefaultConfigTomlConfig(nodeConfig),
|
||||
)
|
||||
storeServer := store.New[T]()
|
||||
grpcServer := grpc.New[T](grpc.OverwriteDefaultConfig(grpcConfig))
|
||||
server := serverv2.NewServer[T](serverCfg, cometServer, grpcServer, storeServer)
|
||||
cometServer := cometbft.NewWithConfigOptions[T](cometbft.OverwriteDefaultConfigTomlConfig(nodeConfig))
|
||||
storeServer := &store.Server[T]{}
|
||||
grpcServer := grpc.NewWithConfigOptions[T](grpc.OverwriteDefaultConfig(grpcConfig))
|
||||
server := serverv2.NewServer[T](serverCfg, cometServer, storeServer, grpcServer)
|
||||
err = server.WriteConfig(filepath.Join(nodeDir, "config"))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -363,7 +358,6 @@ func initTestnetFiles[T transaction.Tx](
|
||||
return err
|
||||
}
|
||||
|
||||
// Update viper root since root dir become rootdir/node/simd
|
||||
serverv2.GetViperFromCmd(cmd).Set(flags.FlagHome, nodeConfig.RootDir)
|
||||
|
||||
cmd.PrintErrf("Successfully initialized %d node directories\n", args.numValidators)
|
||||
|
||||
@@ -7,8 +7,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cosmossdk.io/core/transaction"
|
||||
svrcmd "cosmossdk.io/server/v2"
|
||||
"cosmossdk.io/simapp/v2"
|
||||
"cosmossdk.io/simapp/v2/simdv2/cmd"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
@@ -16,12 +14,13 @@ import (
|
||||
)
|
||||
|
||||
func TestInitTestFilesCmd(t *testing.T) {
|
||||
rootCmd := cmd.NewCometBFTRootCmd[transaction.Tx]()
|
||||
rootCmd.SetArgs([]string{
|
||||
args := []string{
|
||||
"testnet", // Test the testnet init-files command
|
||||
"init-files",
|
||||
fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), // Set keyring-backend to test
|
||||
})
|
||||
|
||||
require.NoError(t, svrcmd.Execute(rootCmd, "", simapp.DefaultNodeHome))
|
||||
}
|
||||
rootCmd, err := cmd.NewRootCmd[transaction.Tx](args...)
|
||||
require.NoError(t, err)
|
||||
rootCmd.SetArgs(args)
|
||||
require.NoError(t, rootCmd.Execute())
|
||||
}
|
||||
|
||||
@@ -1,20 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
clientv2helpers "cosmossdk.io/client/v2/helpers"
|
||||
"cosmossdk.io/core/transaction"
|
||||
serverv2 "cosmossdk.io/server/v2"
|
||||
"cosmossdk.io/simapp/v2"
|
||||
"cosmossdk.io/simapp/v2/simdv2/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
rootCmd := cmd.NewCometBFTRootCmd[transaction.Tx]()
|
||||
if err := serverv2.Execute(rootCmd, clientv2helpers.EnvPrefix, simapp.DefaultNodeHome); err != nil {
|
||||
fmt.Fprintln(rootCmd.OutOrStderr(), err)
|
||||
// reproduce default cobra behavior so that eager parsing of flags is possible.
|
||||
// see: https://github.com/spf13/cobra/blob/e94f6d0dd9a5e5738dca6bce03c4b1207ffbc0ec/command.go#L1082
|
||||
args := os.Args[1:]
|
||||
rootCmd, err := cmd.NewRootCmd[transaction.Tx](args...)
|
||||
if err != nil {
|
||||
if _, pErr := fmt.Fprintln(os.Stderr, err); pErr != nil {
|
||||
panic(errors.Join(err, pErr))
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
if err = rootCmd.Execute(); err != nil {
|
||||
if _, pErr := fmt.Fprintln(rootCmd.OutOrStderr(), err); pErr != nil {
|
||||
panic(errors.Join(err, pErr))
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"cosmossdk.io/core/transaction"
|
||||
"cosmossdk.io/errors"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
@@ -16,7 +17,7 @@ import (
|
||||
const flagGenTxDir = "gentx-dir"
|
||||
|
||||
// CollectGenTxsCmd - return the cobra command to collect genesis transactions
|
||||
func CollectGenTxsCmd(validator types.MessageValidator) *cobra.Command {
|
||||
func CollectGenTxsCmd(validator func([]transaction.Msg) error) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "collect-gentxs",
|
||||
Short: "Collect genesis txs and output a genesis.json file",
|
||||
@@ -50,7 +51,9 @@ func CollectGenTxsCmd(validator types.MessageValidator) *cobra.Command {
|
||||
toPrint := newPrintInfo(config.Moniker, appGenesis.ChainID, nodeID, genTxsDir, json.RawMessage(""))
|
||||
initCfg := types.NewInitConfig(appGenesis.ChainID, genTxsDir, nodeID, valPubKey)
|
||||
|
||||
appMessage, err := genutil.GenAppStateFromConfig(cdc, clientCtx.TxConfig, config, initCfg, appGenesis, validator, clientCtx.ValidatorAddressCodec, clientCtx.AddressCodec)
|
||||
appMessage, err := genutil.GenAppStateFromConfig(
|
||||
cdc, clientCtx.TxConfig, config, initCfg, appGenesis,
|
||||
validator, clientCtx.ValidatorAddressCodec, clientCtx.AddressCodec)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get genesis app state from config")
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"cosmossdk.io/core/transaction"
|
||||
banktypes "cosmossdk.io/x/bank/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/x/genutil"
|
||||
"github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
|
||||
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
|
||||
v2 "github.com/cosmos/cosmos-sdk/x/genutil/v2"
|
||||
@@ -19,17 +19,26 @@ type genesisMM interface {
|
||||
ValidateGenesis(genesisData map[string]json.RawMessage) error
|
||||
}
|
||||
|
||||
type ExportableApp interface {
|
||||
ExportAppStateAndValidators([]string) (v2.ExportedApp, error)
|
||||
LoadHeight(uint64) error
|
||||
}
|
||||
|
||||
// Commands adds core sdk's sub-commands into genesis command.
|
||||
func Commands(genutilModule genutil.AppModule, genMM genesisMM, appExport v2.AppExporter) *cobra.Command {
|
||||
return CommandsWithCustomMigrationMap(genutilModule, genMM, appExport, cli.MigrationMap)
|
||||
func Commands(
|
||||
genTxValidator func([]transaction.Msg) error,
|
||||
genMM genesisMM,
|
||||
exportable ExportableApp,
|
||||
) *cobra.Command {
|
||||
return CommandsWithCustomMigrationMap(genTxValidator, genMM, exportable, cli.MigrationMap)
|
||||
}
|
||||
|
||||
// CommandsWithCustomMigrationMap adds core sdk's sub-commands into genesis command with custom migration map.
|
||||
// This custom migration map can be used by the application to add its own migration map.
|
||||
func CommandsWithCustomMigrationMap(
|
||||
genutilModule genutil.AppModule,
|
||||
genTxValidator func([]transaction.Msg) error,
|
||||
genMM genesisMM,
|
||||
appExport v2.AppExporter,
|
||||
exportable ExportableApp,
|
||||
migrationMap genutiltypes.MigrationMap,
|
||||
) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
@@ -42,10 +51,10 @@ func CommandsWithCustomMigrationMap(
|
||||
cmd.AddCommand(
|
||||
cli.GenTxCmd(genMM, banktypes.GenesisBalancesIterator{}),
|
||||
cli.MigrateGenesisCmd(migrationMap),
|
||||
cli.CollectGenTxsCmd(genutilModule.GenTxValidator()),
|
||||
cli.CollectGenTxsCmd(genTxValidator),
|
||||
cli.ValidateGenesisCmd(genMM),
|
||||
cli.AddGenesisAccountCmd(),
|
||||
ExportCmd(appExport),
|
||||
ExportCmd(exportable),
|
||||
)
|
||||
|
||||
return cmd
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
|
||||
v2 "github.com/cosmos/cosmos-sdk/x/genutil/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -22,7 +21,7 @@ const (
|
||||
)
|
||||
|
||||
// ExportCmd dumps app state to JSON.
|
||||
func ExportCmd(appExporter v2.AppExporter) *cobra.Command {
|
||||
func ExportCmd(app ExportableApp) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "export",
|
||||
Short: "Export state to JSON",
|
||||
@@ -34,7 +33,7 @@ func ExportCmd(appExporter v2.AppExporter) *cobra.Command {
|
||||
return err
|
||||
}
|
||||
|
||||
if appExporter == nil {
|
||||
if app == nil {
|
||||
if _, err := fmt.Fprintln(cmd.ErrOrStderr(), "WARNING: App exporter not defined. Returning genesis file."); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -59,8 +58,12 @@ func ExportCmd(appExporter v2.AppExporter) *cobra.Command {
|
||||
height, _ := cmd.Flags().GetInt64(flagHeight)
|
||||
jailAllowedAddrs, _ := cmd.Flags().GetStringSlice(flagJailAllowedAddrs)
|
||||
outputDocument, _ := cmd.Flags().GetString(flags.FlagOutputDocument)
|
||||
|
||||
exported, err := appExporter(cmd.Context(), height, jailAllowedAddrs)
|
||||
if height != -1 {
|
||||
if err := app.LoadHeight(uint64(height)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
exported, err := app.ExportAppStateAndValidators(jailAllowedAddrs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error exporting state: %w", err)
|
||||
}
|
||||
|
||||
@@ -1,20 +1,11 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// AppExporter is a function that dumps all app state to
|
||||
// JSON-serializable structure and returns the current validator set.
|
||||
type AppExporter func(
|
||||
ctx context.Context,
|
||||
height int64,
|
||||
jailAllowedAddrs []string,
|
||||
) (ExportedApp, error)
|
||||
|
||||
// ExportedApp represents an exported app state, along with
|
||||
// validators, consensus params and latest app height.
|
||||
type ExportedApp struct {
|
||||
|
||||
+20
-10
@@ -26,22 +26,31 @@ func (am AppModule) IsOnePerModuleType() {}
|
||||
|
||||
func init() {
|
||||
appconfig.RegisterModule(&modulev1.Module{},
|
||||
appconfig.Provide(ProvideModule),
|
||||
appconfig.Provide(ProvideModule, ProvideConfig),
|
||||
appconfig.Invoke(PopulateVersionMap),
|
||||
)
|
||||
}
|
||||
|
||||
func ProvideConfig(key depinject.OwnModuleKey) coreserver.ModuleConfigMap {
|
||||
return coreserver.ModuleConfigMap{
|
||||
Module: depinject.ModuleKey(key).Name(),
|
||||
Config: coreserver.ConfigMap{
|
||||
server.FlagUnsafeSkipUpgrades: []int{},
|
||||
flags.FlagHome: "",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type ModuleInputs struct {
|
||||
depinject.In
|
||||
|
||||
Config *modulev1.Module
|
||||
ConfigMap coreserver.ConfigMap
|
||||
Environment appmodule.Environment
|
||||
Cdc codec.Codec
|
||||
AddressCodec address.Codec
|
||||
AppVersionModifier coreserver.VersionModifier
|
||||
ConsensusKeeper types.ConsensusKeeper
|
||||
|
||||
DynamicConfig coreserver.DynamicConfig `optional:"true"`
|
||||
}
|
||||
|
||||
type ModuleOutputs struct {
|
||||
@@ -57,14 +66,15 @@ func ProvideModule(in ModuleInputs) ModuleOutputs {
|
||||
skipUpgradeHeights = make(map[int64]bool)
|
||||
)
|
||||
|
||||
if in.DynamicConfig != nil {
|
||||
skipUpgrades := cast.ToIntSlice(in.DynamicConfig.Get(server.FlagUnsafeSkipUpgrades))
|
||||
for _, h := range skipUpgrades {
|
||||
skipUpgradeHeights[int64(h)] = true
|
||||
}
|
||||
|
||||
homePath = in.DynamicConfig.GetString(flags.FlagHome)
|
||||
skipUpgrades, ok := in.ConfigMap[server.FlagUnsafeSkipUpgrades]
|
||||
if !ok || skipUpgrades == nil {
|
||||
skipUpgrades = []int{}
|
||||
}
|
||||
heights := cast.ToIntSlice(skipUpgrades) // safe to use cast here as we've handled nil case
|
||||
for _, h := range heights {
|
||||
skipUpgradeHeights[int64(h)] = true
|
||||
}
|
||||
homePath = cast.ToString(in.ConfigMap[flags.FlagHome])
|
||||
|
||||
// default to governance authority if not provided
|
||||
authority := authtypes.NewModuleAddress(types.GovModuleName)
|
||||
|
||||
+25
-14
@@ -3,6 +3,8 @@ package validate
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
|
||||
modulev1 "cosmossdk.io/api/cosmos/validate/module/v1"
|
||||
appmodulev2 "cosmossdk.io/core/appmodule/v2"
|
||||
"cosmossdk.io/core/server"
|
||||
@@ -25,17 +27,28 @@ const flagMinGasPricesV2 = "server.minimum-gas-prices"
|
||||
|
||||
func init() {
|
||||
appconfig.RegisterModule(&modulev1.Module{},
|
||||
appconfig.Provide(ProvideModule),
|
||||
appconfig.Provide(ProvideModule, ProvideConfig),
|
||||
)
|
||||
}
|
||||
|
||||
// ProvideConfig specifies the configuration key for the minimum gas prices.
|
||||
// During dependency injection, a configuration map is provided with the key set.
|
||||
func ProvideConfig(key depinject.OwnModuleKey) server.ModuleConfigMap {
|
||||
return server.ModuleConfigMap{
|
||||
Module: depinject.ModuleKey(key).Name(),
|
||||
Config: server.ConfigMap{
|
||||
flagMinGasPricesV2: "",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type ModuleInputs struct {
|
||||
depinject.In
|
||||
|
||||
ModuleConfig *modulev1.Module
|
||||
Environment appmodulev2.Environment
|
||||
TxConfig client.TxConfig
|
||||
DynamicConfig server.DynamicConfig `optional:"true"`
|
||||
ModuleConfig *modulev1.Module
|
||||
Environment appmodulev2.Environment
|
||||
TxConfig client.TxConfig
|
||||
ConfigMap server.ConfigMap
|
||||
|
||||
AccountKeeper ante.AccountKeeper
|
||||
BankKeeper authtypes.BankKeeper
|
||||
@@ -69,17 +82,15 @@ func ProvideModule(in ModuleInputs) ModuleOutputs {
|
||||
unorderedTxValidator *ante.UnorderedTxDecorator
|
||||
)
|
||||
|
||||
if in.DynamicConfig != nil {
|
||||
minGasPricesStr := in.DynamicConfig.GetString(flagMinGasPricesV2)
|
||||
minGasPrices, err = sdk.ParseDecCoins(minGasPricesStr)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("invalid minimum gas prices: %v", err))
|
||||
}
|
||||
|
||||
feeTxValidator = ante.NewDeductFeeDecorator(in.AccountKeeper, in.BankKeeper, in.FeeGrantKeeper, in.TxFeeChecker)
|
||||
feeTxValidator.SetMinGasPrices(minGasPrices) // set min gas price in deduct fee decorator
|
||||
minGasPricesStr := cast.ToString(in.ConfigMap[flagMinGasPricesV2])
|
||||
minGasPrices, err = sdk.ParseDecCoins(minGasPricesStr)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("invalid minimum gas prices: %v", err))
|
||||
}
|
||||
|
||||
feeTxValidator = ante.NewDeductFeeDecorator(in.AccountKeeper, in.BankKeeper, in.FeeGrantKeeper, in.TxFeeChecker)
|
||||
feeTxValidator.SetMinGasPrices(minGasPrices) // set min gas price in deduct fee decorator
|
||||
|
||||
if in.UnorderedTxManager != nil {
|
||||
unorderedTxValidator = ante.NewUnorderedTxDecorator(unorderedtx.DefaultMaxTimeoutDuration, in.UnorderedTxManager, in.Environment, ante.DefaultSha256Cost)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user