From aeb8ee7933f54c9397380bfd9af015b87dfdb519 Mon Sep 17 00:00:00 2001 From: Artur Troian Date: Wed, 6 Sep 2023 09:55:59 -0400 Subject: [PATCH] feat: export genesis to custom file Signed-off-by: Artur Troian --- cmd/akash/cmd/root.go | 40 +++++---- util/server/server.go | 192 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 211 insertions(+), 21 deletions(-) create mode 100644 util/server/server.go diff --git a/cmd/akash/cmd/root.go b/cmd/akash/cmd/root.go index e2f4ee577d..b214557850 100644 --- a/cmd/akash/cmd/root.go +++ b/cmd/akash/cmd/root.go @@ -16,7 +16,7 @@ import ( "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" + sdkserver "github.com/cosmos/cosmos-sdk/server" servertypes "github.com/cosmos/cosmos-sdk/server/types" "github.com/cosmos/cosmos-sdk/simapp/params" "github.com/cosmos/cosmos-sdk/snapshots" @@ -39,6 +39,7 @@ import ( "github.com/akash-network/node/cmd/akash/cmd/testnetify" ecmd "github.com/akash-network/node/events/cmd" utilcli "github.com/akash-network/node/util/cli" + "github.com/akash-network/node/util/server" ) // NewRootCmd creates a new root command for akash. It is called once in the @@ -88,10 +89,10 @@ func Execute(rootCmd *cobra.Command, envPrefix string) error { // and a Tendermint RPC. This requires the use of a pointer reference when // getting and setting the client.Context. Ideally, we utilize // https://github.com/spf13/cobra/pull/1118. - srvCtx := server.NewDefaultContext() + srvCtx := sdkserver.NewDefaultContext() ctx := context.Background() ctx = context.WithValue(ctx, client.ClientContextKey, &client.Context{}) - ctx = context.WithValue(ctx, server.ServerContextKey, srvCtx) + ctx = context.WithValue(ctx, sdkserver.ServerContextKey, srvCtx) rootCmd.PersistentFlags().String(flags.FlagLogLevel, zerolog.InfoLevel.String(), "The logging level (trace|debug|info|warn|error|fatal|panic)") rootCmd.PersistentFlags().String(flags.FlagLogFormat, tmcfg.LogFormatPlain, "The logging format (json|plain)") @@ -121,16 +122,13 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) { AddGenesisAccountCmd(app.DefaultHome), tmcli.NewCompletionCmd(rootCmd, true), debugCmd, + sdkserver.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Marshaler), ) + rootCmd.AddCommand(server.Commands(app.DefaultHome, newApp, createAppAndExport, addModuleInitFlags)...) + rootCmd.SetOut(rootCmd.OutOrStdout()) rootCmd.SetErr(rootCmd.ErrOrStderr()) - - server.AddCommands(rootCmd, app.DefaultHome, newApp, createAppAndExport, addModuleInitFlags) - - rootCmd.AddCommand( - server.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Marshaler)) - } func addModuleInitFlags(startCmd *cobra.Command) { @@ -140,16 +138,16 @@ func addModuleInitFlags(startCmd *cobra.Command) { func 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)) { + if cast.ToBool(appOpts.Get(sdkserver.FlagInterBlockCache)) { cache = store.NewCommitKVStoreCacheManager() } skipUpgradeHeights := make(map[int64]bool) - for _, h := range cast.ToIntSlice(appOpts.Get(server.FlagUnsafeSkipUpgrades)) { + for _, h := range cast.ToIntSlice(appOpts.Get(sdkserver.FlagUnsafeSkipUpgrades)) { skipUpgradeHeights[int64(h)] = true } - pruningOpts, err := server.GetPruningOptionsFromFlags(appOpts) + pruningOpts, err := sdkserver.GetPruningOptionsFromFlags(appOpts) if err != nil { panic(err) } @@ -165,20 +163,20 @@ func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts serverty } return app.NewApp( - logger, db, traceStore, true, cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)), skipUpgradeHeights, + logger, db, traceStore, true, cast.ToUint(appOpts.Get(sdkserver.FlagInvCheckPeriod)), skipUpgradeHeights, cast.ToString(appOpts.Get(flags.FlagHome)), 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.SetMinGasPrices(cast.ToString(appOpts.Get(sdkserver.FlagMinGasPrices))), + baseapp.SetHaltHeight(cast.ToUint64(appOpts.Get(sdkserver.FlagHaltHeight))), + baseapp.SetHaltTime(cast.ToUint64(appOpts.Get(sdkserver.FlagHaltTime))), + baseapp.SetMinRetainBlocks(cast.ToUint64(appOpts.Get(sdkserver.FlagMinRetainBlocks))), baseapp.SetInterBlockCache(cache), - baseapp.SetTrace(cast.ToBool(appOpts.Get(server.FlagTrace))), - baseapp.SetIndexEvents(cast.ToStringSlice(appOpts.Get(server.FlagIndexEvents))), + baseapp.SetTrace(cast.ToBool(appOpts.Get(sdkserver.FlagTrace))), + baseapp.SetIndexEvents(cast.ToStringSlice(appOpts.Get(sdkserver.FlagIndexEvents))), baseapp.SetSnapshotStore(snapshotStore), - baseapp.SetSnapshotInterval(cast.ToUint64(appOpts.Get(server.FlagStateSyncSnapshotInterval))), - baseapp.SetSnapshotKeepRecent(cast.ToUint32(appOpts.Get(server.FlagStateSyncSnapshotKeepRecent))), + baseapp.SetSnapshotInterval(cast.ToUint64(appOpts.Get(sdkserver.FlagStateSyncSnapshotInterval))), + baseapp.SetSnapshotKeepRecent(cast.ToUint32(appOpts.Get(sdkserver.FlagStateSyncSnapshotKeepRecent))), ) } diff --git a/util/server/server.go b/util/server/server.go new file mode 100644 index 0000000000..fb8c51919b --- /dev/null +++ b/util/server/server.go @@ -0,0 +1,192 @@ +package server + +import ( + "fmt" + "io" + "os" + "path/filepath" + + "github.com/cosmos/cosmos-sdk/client/flags" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/spf13/cobra" + tmjson "github.com/tendermint/tendermint/libs/json" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtypes "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tm-db" + + tmcmd "github.com/tendermint/tendermint/cmd/cometbft/commands" + + sdkserver "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/server/types" + "github.com/cosmos/cosmos-sdk/version" +) + +const ( + // Tendermint full-node start flags + flagTraceStore = "trace-store" + flagToFile = "to-file" +) + +// Commands server commands +func Commands(defaultNodeHome string, appCreator types.AppCreator, appExport types.AppExporter, addStartFlags types.ModuleInitFlags) []*cobra.Command { + tendermintCmd := &cobra.Command{ + Use: "tendermint", + Short: "Tendermint subcommands", + } + + tendermintCmd.AddCommand( + sdkserver.ShowNodeIDCmd(), + sdkserver.ShowValidatorCmd(), + sdkserver.ShowAddressCmd(), + sdkserver.VersionCmd(), + tmcmd.ResetAllCmd, + tmcmd.ResetStateCmd, + ) + + startCmd := sdkserver.StartCmd(appCreator, defaultNodeHome) + addStartFlags(startCmd) + + cmds := []*cobra.Command{ + startCmd, + tendermintCmd, + ExportCmd(appExport, defaultNodeHome), + version.NewVersionCommand(), + sdkserver.NewRollbackCmd(appCreator, defaultNodeHome), + } + + return cmds +} + +// ExportCmd dumps app state to JSON. +func ExportCmd(appExporter types.AppExporter, defaultNodeHome string) *cobra.Command { + cmd := &cobra.Command{ + Use: "export", + Short: "Export state to JSON", + RunE: func(cmd *cobra.Command, args []string) error { + serverCtx := sdkserver.GetServerContextFromCmd(cmd) + config := serverCtx.Config + + homeDir, _ := cmd.Flags().GetString(flags.FlagHome) + config.SetRoot(homeDir) + + if _, err := os.Stat(config.GenesisFile()); os.IsNotExist(err) { + return err + } + + db, err := openDB(config.RootDir) + if err != nil { + return err + } + + outFile := os.Stdout + if toFile, _ := cmd.Flags().GetString(flagToFile); toFile != "" { + outFile, err = os.Create(toFile) + if err != nil { + return err + } + } + + defer func() { + if outFile != os.Stdout { + _ = outFile.Close() + } + }() + + if appExporter == nil { + if _, err := fmt.Fprintln(os.Stderr, "WARNING: App exporter not defined. Returning genesis file."); err != nil { + return err + } + + genesis, err := os.ReadFile(config.GenesisFile()) + if err != nil { + return err + } + + _, err = fmt.Fprintln(outFile, string(genesis)) + if err != nil { + return err + } + + return nil + } + + traceWriterFile, _ := cmd.Flags().GetString(flagTraceStore) + traceWriter, err := openTraceWriter(traceWriterFile) + if err != nil { + return err + } + + height, _ := cmd.Flags().GetInt64(sdkserver.FlagHeight) + forZeroHeight, _ := cmd.Flags().GetBool(sdkserver.FlagForZeroHeight) + jailAllowedAddrs, _ := cmd.Flags().GetStringSlice(sdkserver.FlagJailAllowedAddrs) + + exported, err := appExporter(serverCtx.Logger, db, traceWriter, height, forZeroHeight, jailAllowedAddrs, serverCtx.Viper) + if err != nil { + return fmt.Errorf("error exporting state: %v", err) + } + + doc, err := tmtypes.GenesisDocFromFile(serverCtx.Config.GenesisFile()) + if err != nil { + return err + } + + doc.AppState = exported.AppState + doc.Validators = exported.Validators + doc.InitialHeight = exported.Height + doc.ConsensusParams = &tmproto.ConsensusParams{ + Block: tmproto.BlockParams{ + MaxBytes: exported.ConsensusParams.Block.MaxBytes, + MaxGas: exported.ConsensusParams.Block.MaxGas, + TimeIotaMs: doc.ConsensusParams.Block.TimeIotaMs, + }, + Evidence: tmproto.EvidenceParams{ + MaxAgeNumBlocks: exported.ConsensusParams.Evidence.MaxAgeNumBlocks, + MaxAgeDuration: exported.ConsensusParams.Evidence.MaxAgeDuration, + MaxBytes: exported.ConsensusParams.Evidence.MaxBytes, + }, + Validator: tmproto.ValidatorParams{ + PubKeyTypes: exported.ConsensusParams.Validator.PubKeyTypes, + }, + } + + // NOTE: Tendermint uses a custom JSON decoder for GenesisDoc + // (except for stuff inside AppState). Inside AppState, we're free + // to encode as protobuf or amino. + encoded, err := tmjson.Marshal(doc) + if err != nil { + return err + } + + _, err = fmt.Fprintln(outFile, string(sdk.MustSortJSON(encoded))) + if err != nil { + return err + } + + return nil + }, + } + + cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") + cmd.Flags().Int64(sdkserver.FlagHeight, -1, "Export state from a particular height (-1 means latest height)") + cmd.Flags().Bool(sdkserver.FlagForZeroHeight, false, "Export state to start at height zero (perform preprocessing)") + cmd.Flags().StringSlice(sdkserver.FlagJailAllowedAddrs, []string{}, "Comma-separated list of operator addresses of jailed validators to unjail") + cmd.Flags().String(flagToFile, "", "Export Genesis to specified file") + + return cmd +} + +func openDB(rootDir string) (dbm.DB, error) { + dataDir := filepath.Join(rootDir, "data") + return sdk.NewLevelDB("application", dataDir) +} + +func openTraceWriter(traceWriterFile string) (w io.Writer, err error) { + if traceWriterFile == "" { + return + } + return os.OpenFile( + traceWriterFile, + os.O_WRONLY|os.O_APPEND|os.O_CREATE, + 0o666, + ) +}