package cmd import ( "errors" "io" "os" "path/filepath" "github.com/spf13/cast" "github.com/spf13/cobra" tmcfg "github.com/tendermint/tendermint/config" tmcli "github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/baseapp" "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/flags" "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/server" serverconfig "github.com/cosmos/cosmos-sdk/server/config" servertypes "github.com/cosmos/cosmos-sdk/server/types" "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/simapp/params" "github.com/cosmos/cosmos-sdk/snapshots" snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/crisis" genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" ) // NewRootCmd creates a new root command for simd. It is called once in the // main function. func NewRootCmd() (*cobra.Command, params.EncodingConfig) { encodingConfig := simapp.MakeTestEncodingConfig() initClientCtx := client.Context{}. WithCodec(encodingConfig.Codec). WithInterfaceRegistry(encodingConfig.InterfaceRegistry). WithTxConfig(encodingConfig.TxConfig). WithLegacyAmino(encodingConfig.Amino). WithInput(os.Stdin). WithAccountRetriever(types.AccountRetriever{}). WithHomeDir(simapp.DefaultNodeHome). WithViper("") // In simapp, we don't use any prefix for env variables. rootCmd := &cobra.Command{ Use: "simd", Short: "simulation app", PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { // set the default command outputs cmd.SetOut(cmd.OutOrStdout()) cmd.SetErr(cmd.ErrOrStderr()) initClientCtx, err := client.ReadPersistentCommandFlags(initClientCtx, cmd.Flags()) if err != nil { return err } initClientCtx, err = config.ReadFromClientConfig(initClientCtx) if err != nil { return err } if err := client.SetCmdClientContextHandler(initClientCtx, cmd); err != nil { return err } customAppTemplate, customAppConfig := initAppConfig() customTMConfig := initTendermintConfig() return server.InterceptConfigsPreRunHandler(cmd, customAppTemplate, customAppConfig, customTMConfig) }, } initRootCmd(rootCmd, encodingConfig) return rootCmd, encodingConfig } // initTendermintConfig helps to override default Tendermint Config values. // return tmcfg.DefaultConfig if no custom configuration is required for the application. func initTendermintConfig() *tmcfg.Config { cfg := tmcfg.DefaultConfig() // these values put a higher strain on node memory // cfg.P2P.MaxNumInboundPeers = 100 // cfg.P2P.MaxNumOutboundPeers = 40 return cfg } // initAppConfig helps to override default appConfig template and configs. // return "", nil if no custom configuration is required for the application. func initAppConfig() (string, interface{}) { // The following code snippet is just for reference. // WASMConfig defines configuration for the wasm module. type WASMConfig struct { // This is the maximum sdk gas (wasm and storage) that we allow for any x/wasm "smart" queries QueryGasLimit uint64 `mapstructure:"query_gas_limit"` // Address defines the gRPC-web server to listen on LruSize uint64 `mapstructure:"lru_size"` } type CustomAppConfig struct { serverconfig.Config WASM WASMConfig `mapstructure:"wasm"` } // Optionally allow the chain developer to overwrite the SDK's default // server config. srvCfg := serverconfig.DefaultConfig() // The SDK's default minimum gas price is set to "" (empty value) inside // app.toml. If left empty by validators, the node will halt on startup. // However, the chain developer can set a default app.toml value for their // validators here. // // In summary: // - if you leave srvCfg.MinGasPrices = "", all validators MUST tweak their // own app.toml config, // - if you set srvCfg.MinGasPrices non-empty, validators CAN tweak their // own app.toml to override, or use this default value. // // In simapp, we set the min gas prices to 0. srvCfg.MinGasPrices = "0stake" customAppConfig := CustomAppConfig{ Config: *srvCfg, WASM: WASMConfig{ LruSize: 1, QueryGasLimit: 300000, }, } customAppTemplate := serverconfig.DefaultConfigTemplate + ` [wasm] # This is the maximum sdk gas (wasm and storage) that we allow for any x/wasm "smart" queries query_gas_limit = 300000 # This is the number of wasm vm instances we keep cached in memory for speed-up # Warning: this is currently unstable and may lead to crashes, best to keep for 0 unless testing locally lru_size = 0` return customAppTemplate, customAppConfig } func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) { cfg := sdk.GetConfig() cfg.Seal() rootCmd.AddCommand( genutilcli.InitCmd(simapp.ModuleBasics, simapp.DefaultNodeHome), genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, simapp.DefaultNodeHome), genutilcli.MigrateGenesisCmd(), genutilcli.GenTxCmd(simapp.ModuleBasics, encodingConfig.TxConfig, banktypes.GenesisBalancesIterator{}, simapp.DefaultNodeHome), genutilcli.ValidateGenesisCmd(simapp.ModuleBasics), AddGenesisAccountCmd(simapp.DefaultNodeHome), tmcli.NewCompletionCmd(rootCmd, true), NewTestnetCmd(simapp.ModuleBasics, banktypes.GenesisBalancesIterator{}), debug.Cmd(), config.Cmd(), ) a := appCreator{encodingConfig} server.AddCommands(rootCmd, simapp.DefaultNodeHome, a.newApp, a.appExport, addModuleInitFlags) // add keybase, auxiliary RPC, query, and tx child commands rootCmd.AddCommand( rpc.StatusCommand(), queryCommand(), txCommand(), keys.Commands(simapp.DefaultNodeHome), ) // add rosetta rootCmd.AddCommand(server.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Codec)) } func addModuleInitFlags(startCmd *cobra.Command) { crisis.AddModuleInitFlags(startCmd) } func queryCommand() *cobra.Command { cmd := &cobra.Command{ Use: "query", Aliases: []string{"q"}, Short: "Querying subcommands", DisableFlagParsing: false, SuggestionsMinimumDistance: 2, RunE: client.ValidateCmd, } cmd.AddCommand( authcmd.GetAccountCmd(), rpc.ValidatorCommand(), rpc.BlockCommand(), authcmd.QueryTxsByEventsCmd(), authcmd.QueryTxCmd(), ) simapp.ModuleBasics.AddQueryCommands(cmd) cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID") return cmd } func txCommand() *cobra.Command { cmd := &cobra.Command{ Use: "tx", Short: "Transactions subcommands", DisableFlagParsing: false, SuggestionsMinimumDistance: 2, RunE: client.ValidateCmd, } cmd.AddCommand( authcmd.GetSignCommand(), authcmd.GetSignBatchCommand(), authcmd.GetMultiSignCommand(), authcmd.GetMultiSignBatchCmd(), authcmd.GetValidateSignaturesCommand(), authcmd.GetBroadcastCommand(), authcmd.GetEncodeCommand(), authcmd.GetDecodeCommand(), authcmd.GetAuxToFeeCommand(), ) simapp.ModuleBasics.AddTxCommands(cmd) cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID") return cmd } type appCreator struct { encCfg params.EncodingConfig } // newApp is an appCreator func (a appCreator) newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts servertypes.AppOptions) servertypes.Application { var cache sdk.MultiStorePersistentCache if cast.ToBool(appOpts.Get(server.FlagInterBlockCache)) { cache = store.NewCommitKVStoreCacheManager() } skipUpgradeHeights := make(map[int64]bool) for _, h := range cast.ToIntSlice(appOpts.Get(server.FlagUnsafeSkipUpgrades)) { skipUpgradeHeights[int64(h)] = true } pruningOpts, err := server.GetPruningOptionsFromFlags(appOpts) if err != nil { panic(err) } snapshotDir := filepath.Join(cast.ToString(appOpts.Get(flags.FlagHome)), "data", "snapshots") snapshotDB, err := dbm.NewDB("metadata", server.GetAppDBBackend(appOpts), snapshotDir) if err != nil { panic(err) } snapshotStore, err := snapshots.NewStore(snapshotDB, snapshotDir) if err != nil { panic(err) } snapshotOptions := snapshottypes.NewSnapshotOptions( cast.ToUint64(appOpts.Get(server.FlagStateSyncSnapshotInterval)), cast.ToUint32(appOpts.Get(server.FlagStateSyncSnapshotKeepRecent)), ) return simapp.NewSimApp( logger, db, traceStore, true, skipUpgradeHeights, cast.ToString(appOpts.Get(flags.FlagHome)), cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)), a.encCfg, appOpts, baseapp.SetPruning(pruningOpts), baseapp.SetMinGasPrices(cast.ToString(appOpts.Get(server.FlagMinGasPrices))), baseapp.SetHaltHeight(cast.ToUint64(appOpts.Get(server.FlagHaltHeight))), baseapp.SetHaltTime(cast.ToUint64(appOpts.Get(server.FlagHaltTime))), baseapp.SetMinRetainBlocks(cast.ToUint64(appOpts.Get(server.FlagMinRetainBlocks))), baseapp.SetInterBlockCache(cache), baseapp.SetTrace(cast.ToBool(appOpts.Get(server.FlagTrace))), baseapp.SetIndexEvents(cast.ToStringSlice(appOpts.Get(server.FlagIndexEvents))), baseapp.SetSnapshot(snapshotStore, snapshotOptions), ) } // appExport creates a new simapp (optionally at a given height) // and exports state. func (a appCreator) appExport( logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailAllowedAddrs []string, appOpts servertypes.AppOptions) (servertypes.ExportedApp, error) { var simApp *simapp.SimApp homePath, ok := appOpts.Get(flags.FlagHome).(string) if !ok || homePath == "" { return servertypes.ExportedApp{}, errors.New("application home not set") } if height != -1 { simApp = simapp.NewSimApp(logger, db, traceStore, false, map[int64]bool{}, homePath, uint(1), a.encCfg, appOpts) if err := simApp.LoadHeight(height); err != nil { return servertypes.ExportedApp{}, err } } else { simApp = simapp.NewSimApp(logger, db, traceStore, true, map[int64]bool{}, homePath, uint(1), a.encCfg, appOpts) } return simApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs) }