From 9ebda4edb93f78c2886f928720b3ae7783d6f846 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Sun, 5 Jul 2020 12:56:17 -0400 Subject: [PATCH] Server/simd: Viper Removal (#6599) * init commit * remove viper from tm cmds * updates * Undo x/bank/client/cli/tx.go * Fix unit tests * lint++ * rename var * Fix genutil test * fix test * prefer cmd.Flags() over direct viper usage * update * fix ABCI error tests * fix integration tests * Add viper to context * fix build * fix unit test * Implement and use AppOptions * Revert Redact godoc Co-authored-by: Alessio Treglia --- baseapp/abci.go | 8 +- baseapp/baseapp.go | 7 ++ baseapp/options.go | 5 + go.mod | 1 + server/config/config.go | 46 +++++----- server/config/toml.go | 4 +- server/constructors.go | 13 ++- server/export.go | 15 ++- server/export_test.go | 11 +-- server/pruning.go | 11 ++- server/pruning_test.go | 33 ++++--- server/start.go | 50 +++++----- server/test_helpers.go | 26 ------ server/tm_cmds.go | 12 +-- server/util.go | 127 ++++++++++++++------------ simapp/cmd/simd/genaccounts.go | 38 ++++---- simapp/cmd/simd/main.go | 45 ++++----- simapp/cmd/simd/testnet.go | 54 ++++------- types/errors/abci.go | 16 ++-- types/errors/abci_test.go | 132 +++++++++++---------------- x/genutil/client/cli/collect.go | 5 +- x/genutil/client/cli/init_test.go | 11 +-- x/genutil/client/cli/migrate_test.go | 2 +- x/staking/client/cli/tx_test.go | 4 +- 24 files changed, 319 insertions(+), 357 deletions(-) diff --git a/baseapp/abci.go b/baseapp/abci.go index a357fba055..ea0fad4718 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -172,7 +172,7 @@ func (app *BaseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { tx, err := app.txDecoder(req.Tx) if err != nil { - return sdkerrors.ResponseCheckTx(err, 0, 0) + return sdkerrors.ResponseCheckTx(err, 0, 0, app.trace) } var mode runTxMode @@ -190,7 +190,7 @@ func (app *BaseApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { gInfo, result, err := app.runTx(mode, req.Tx, tx) if err != nil { - return sdkerrors.ResponseCheckTx(err, gInfo.GasWanted, gInfo.GasUsed) + return sdkerrors.ResponseCheckTx(err, gInfo.GasWanted, gInfo.GasUsed, app.trace) } return abci.ResponseCheckTx{ @@ -212,7 +212,7 @@ func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx tx, err := app.txDecoder(req.Tx) if err != nil { - return sdkerrors.ResponseDeliverTx(err, 0, 0) + return sdkerrors.ResponseDeliverTx(err, 0, 0, app.trace) } gInfo := sdk.GasInfo{} @@ -228,7 +228,7 @@ func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx gInfo, result, err := app.runTx(runTxModeDeliver, req.Tx, tx) if err != nil { resultStr = "failed" - return sdkerrors.ResponseDeliverTx(err, gInfo.GasWanted, gInfo.GasUsed) + return sdkerrors.ResponseDeliverTx(err, gInfo.GasWanted, gInfo.GasUsed, app.trace) } return abci.ResponseDeliverTx{ diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index fe58222886..8da9a08ca2 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -95,6 +95,9 @@ type BaseApp struct { // nolint: maligned // recovery handler for app.runTx method runTxRecoveryMiddleware recoveryMiddleware + + // trace set will return full stack traces for errors in ABCI Log field + trace bool } // NewBaseApp returns a reference to an initialized BaseApp. It accepts a @@ -276,6 +279,10 @@ func (app *BaseApp) setInterBlockCache(cache sdk.MultiStorePersistentCache) { app.interBlockCache = cache } +func (app *BaseApp) setTrace(trace bool) { + app.trace = trace +} + // Router returns the router of the BaseApp. func (app *BaseApp) Router() sdk.Router { if app.sealed { diff --git a/baseapp/options.go b/baseapp/options.go index 08c77a05fe..5278598f8b 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -38,6 +38,11 @@ func SetHaltTime(haltTime uint64) func(*BaseApp) { return func(bap *BaseApp) { bap.setHaltTime(haltTime) } } +// SetTrace will turn on or off trace flag +func SetTrace(trace bool) func(*BaseApp) { + return func(app *BaseApp) { app.setTrace(trace) } +} + // SetInterBlockCache provides a BaseApp option function that sets the // inter-block cache. func SetInterBlockCache(cache sdk.MultiStorePersistentCache) func(*BaseApp) { diff --git a/go.mod b/go.mod index 8a96896632..b783c468c6 100644 --- a/go.mod +++ b/go.mod @@ -28,6 +28,7 @@ require ( github.com/rakyll/statik v0.1.7 github.com/regen-network/cosmos-proto v0.3.0 github.com/spf13/afero v1.2.2 // indirect + github.com/spf13/cast v1.3.0 github.com/spf13/cobra v1.0.0 github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 diff --git a/server/config/config.go b/server/config/config.go index 83cac3c9fc..d78072d884 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -138,8 +138,8 @@ func DefaultConfig() *Config { } // GetConfig returns a fully parsed Config object. -func GetConfig() Config { - globalLabelsRaw := viper.Get("telemetry.global-labels").([]interface{}) +func GetConfig(v *viper.Viper) Config { + globalLabelsRaw := v.Get("telemetry.global-labels").([]interface{}) globalLabels := make([][]string, 0, len(globalLabelsRaw)) for _, glr := range globalLabelsRaw { labelsRaw := glr.([]interface{}) @@ -150,32 +150,32 @@ func GetConfig() Config { return Config{ BaseConfig: BaseConfig{ - MinGasPrices: viper.GetString("minimum-gas-prices"), - InterBlockCache: viper.GetBool("inter-block-cache"), - Pruning: viper.GetString("pruning"), - PruningKeepRecent: viper.GetString("pruning-keep-recent"), - PruningKeepEvery: viper.GetString("pruning-keep-every"), - PruningInterval: viper.GetString("pruning-interval"), - HaltHeight: viper.GetUint64("halt-height"), - HaltTime: viper.GetUint64("halt-time"), + MinGasPrices: v.GetString("minimum-gas-prices"), + InterBlockCache: v.GetBool("inter-block-cache"), + Pruning: v.GetString("pruning"), + PruningKeepRecent: v.GetString("pruning-keep-recent"), + PruningKeepEvery: v.GetString("pruning-keep-every"), + PruningInterval: v.GetString("pruning-interval"), + HaltHeight: v.GetUint64("halt-height"), + HaltTime: v.GetUint64("halt-time"), }, Telemetry: telemetry.Config{ - ServiceName: viper.GetString("telemetry.service-name"), - Enabled: viper.GetBool("telemetry.enabled"), - EnableHostname: viper.GetBool("telemetry.enable-hostname"), - EnableHostnameLabel: viper.GetBool("telemetry.enable-hostname-label"), - EnableServiceLabel: viper.GetBool("telemetry.enable-service-label"), - PrometheusRetentionTime: viper.GetInt64("telemetry.prometheus-retention-time"), + ServiceName: v.GetString("telemetry.service-name"), + Enabled: v.GetBool("telemetry.enabled"), + EnableHostname: v.GetBool("telemetry.enable-hostname"), + EnableHostnameLabel: v.GetBool("telemetry.enable-hostname-label"), + EnableServiceLabel: v.GetBool("telemetry.enable-service-label"), + PrometheusRetentionTime: v.GetInt64("telemetry.prometheus-retention-time"), GlobalLabels: globalLabels, }, API: APIConfig{ - Enable: viper.GetBool("api.enable"), - Address: viper.GetString("api.address"), - MaxOpenConnections: viper.GetUint("api.max-open-connections"), - RPCReadTimeout: viper.GetUint("api.rpc-read-timeout"), - RPCWriteTimeout: viper.GetUint("api.rpc-write-timeout"), - RPCMaxBodyBytes: viper.GetUint("api.rpc-max-body-bytes"), - EnableUnsafeCORS: viper.GetBool("api.enabled-unsafe-cors"), + Enable: v.GetBool("api.enable"), + Address: v.GetString("api.address"), + MaxOpenConnections: v.GetUint("api.max-open-connections"), + RPCReadTimeout: v.GetUint("api.rpc-read-timeout"), + RPCWriteTimeout: v.GetUint("api.rpc-write-timeout"), + RPCMaxBodyBytes: v.GetUint("api.rpc-max-body-bytes"), + EnableUnsafeCORS: v.GetBool("api.enabled-unsafe-cors"), }, } } diff --git a/server/config/toml.go b/server/config/toml.go index 37120491f5..31fe268ec2 100644 --- a/server/config/toml.go +++ b/server/config/toml.go @@ -127,9 +127,9 @@ func init() { // ParseConfig retrieves the default environment configuration for the // application. -func ParseConfig() (*Config, error) { +func ParseConfig(v *viper.Viper) (*Config, error) { conf := DefaultConfig() - err := viper.Unmarshal(conf) + err := v.Unmarshal(conf) return conf, err } diff --git a/server/constructors.go b/server/constructors.go index 1b62819433..6ff44a4402 100644 --- a/server/constructors.go +++ b/server/constructors.go @@ -16,6 +16,17 @@ import ( ) type ( + // AppOptions defines an interface that is passed into an application + // constructor, typically used to set BaseApp options that are either supplied + // via config file or through CLI arguments/flags. The underlying implementation + // is defined by the server package and is typically implemented via a Viper + // literal defined on the server Context. Note, casting Get calls may not yield + // the expected types and could result in type assertion errors. It is recommend + // to either use the cast package or perform manual conversion for safety. + AppOptions interface { + Get(string) interface{} + } + // Application defines an application interface that wraps abci.Application. // The interface defines the necessary contracts to be implemented in order // to fully bootstrap and start an application. @@ -27,7 +38,7 @@ type ( // AppCreator is a function that allows us to lazily initialize an // application using various configurations. - AppCreator func(log.Logger, dbm.DB, io.Writer) Application + AppCreator func(log.Logger, dbm.DB, io.Writer, AppOptions) Application // AppExporter is a function that dumps all app state to // JSON-serializable structure and returns the current validator set. diff --git a/server/export.go b/server/export.go index 521e9b9b25..91843e1819 100644 --- a/server/export.go +++ b/server/export.go @@ -8,7 +8,6 @@ import ( "os" "github.com/spf13/cobra" - "github.com/spf13/viper" tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/client/flags" @@ -29,9 +28,9 @@ func ExportCmd(ctx *Context, cdc codec.JSONMarshaler, appExporter AppExporter) * Short: "Export state to JSON", RunE: func(cmd *cobra.Command, args []string) error { config := ctx.Config - config.SetRoot(viper.GetString(flags.FlagHome)) - traceWriterFile := viper.GetString(flagTraceStore) + homeDir, _ := cmd.Flags().GetString(flags.FlagHome) + config.SetRoot(homeDir) db, err := openDB(config.RootDir) if err != nil { @@ -52,14 +51,15 @@ func ExportCmd(ctx *Context, cdc codec.JSONMarshaler, appExporter AppExporter) * return nil } + traceWriterFile, _ := cmd.Flags().GetString(flagTraceStore) traceWriter, err := openTraceWriter(traceWriterFile) if err != nil { return err } - height := viper.GetInt64(flagHeight) - forZeroHeight := viper.GetBool(flagForZeroHeight) - jailWhiteList := viper.GetStringSlice(flagJailWhitelist) + height, _ := cmd.Flags().GetInt64(flagHeight) + forZeroHeight, _ := cmd.Flags().GetBool(flagForZeroHeight) + jailWhiteList, _ := cmd.Flags().GetStringSlice(flagJailWhitelist) appState, validators, cp, err := appExporter(ctx.Logger, db, traceWriter, height, forZeroHeight, jailWhiteList) if err != nil { @@ -98,11 +98,10 @@ func ExportCmd(ctx *Context, cdc codec.JSONMarshaler, appExporter AppExporter) * }, } + cmd.Flags().String(flags.FlagHome, "", "The application home directory") cmd.Flags().Int64(flagHeight, -1, "Export state from a particular height (-1 means latest height)") cmd.Flags().Bool(flagForZeroHeight, false, "Export state to start at height zero (perform preproccessing)") cmd.Flags().StringSlice(flagJailWhitelist, []string{}, "List of validators to not jail state export") - cmd.SetOut(cmd.OutOrStdout()) - cmd.SetErr(cmd.OutOrStderr()) return cmd } diff --git a/server/export_test.go b/server/export_test.go index b8d4cd7f88..98a5904b07 100644 --- a/server/export_test.go +++ b/server/export_test.go @@ -3,6 +3,7 @@ package server import ( "bytes" "encoding/json" + "fmt" "io" "os" "path" @@ -10,8 +11,6 @@ import ( "github.com/stretchr/testify/require" - "github.com/spf13/viper" - abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" tmtypes "github.com/tendermint/tendermint/types" @@ -62,12 +61,8 @@ func TestExportCmd_ConsensusParams(t *testing.T) { output := &bytes.Buffer{} cmd.SetOut(output) - - viper.Set(flags.FlagHome, tempDir) - err = cmd.RunE(cmd, []string{}) - if err != nil { - t.Errorf("error: %s", err) - } + cmd.SetArgs([]string{fmt.Sprintf("--%s=%s", flags.FlagHome, tempDir)}) + require.NoError(t, cmd.Execute()) var exportedGenDoc tmtypes.GenesisDoc err = app.Codec().UnmarshalJSON(output.Bytes(), &exportedGenDoc) diff --git a/server/pruning.go b/server/pruning.go index 26497f4b88..ca4f9d6e57 100644 --- a/server/pruning.go +++ b/server/pruning.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/spf13/viper" + "github.com/spf13/cast" "github.com/cosmos/cosmos-sdk/store" "github.com/cosmos/cosmos-sdk/store/types" @@ -13,8 +13,8 @@ import ( // GetPruningOptionsFromFlags parses command flags and returns the correct // PruningOptions. If a pruning strategy is provided, that will be parsed and // returned, otherwise, it is assumed custom pruning options are provided. -func GetPruningOptionsFromFlags() (types.PruningOptions, error) { - strategy := strings.ToLower(viper.GetString(FlagPruning)) +func GetPruningOptionsFromFlags(appOpts AppOptions) (types.PruningOptions, error) { + strategy := strings.ToLower(cast.ToString(appOpts.Get(FlagPruning))) switch strategy { case types.PruningOptionDefault, types.PruningOptionNothing, types.PruningOptionEverything: @@ -22,8 +22,9 @@ func GetPruningOptionsFromFlags() (types.PruningOptions, error) { case types.PruningOptionCustom: opts := types.NewPruningOptions( - viper.GetUint64(FlagPruningKeepRecent), - viper.GetUint64(FlagPruningKeepEvery), viper.GetUint64(FlagPruningInterval), + cast.ToUint64(appOpts.Get(FlagPruningKeepRecent)), + cast.ToUint64(appOpts.Get(FlagPruningKeepEvery)), + cast.ToUint64(appOpts.Get(FlagPruningInterval)), ) if err := opts.Validate(); err != nil { diff --git a/server/pruning_test.go b/server/pruning_test.go index d05f2d2c29..baef9b5916 100644 --- a/server/pruning_test.go +++ b/server/pruning_test.go @@ -12,24 +12,29 @@ import ( func TestGetPruningOptionsFromFlags(t *testing.T) { tests := []struct { name string - initParams func() + initParams func() *viper.Viper expectedOptions types.PruningOptions wantErr bool }{ { name: FlagPruning, - initParams: func() { - viper.Set(FlagPruning, types.PruningOptionNothing) + initParams: func() *viper.Viper { + v := viper.New() + v.Set(FlagPruning, types.PruningOptionNothing) + return v }, expectedOptions: types.PruneNothing, }, { name: "custom pruning options", - initParams: func() { - viper.Set(FlagPruning, types.PruningOptionCustom) - viper.Set(FlagPruningKeepRecent, 1234) - viper.Set(FlagPruningKeepEvery, 4321) - viper.Set(FlagPruningInterval, 10) + initParams: func() *viper.Viper { + v := viper.New() + v.Set(FlagPruning, types.PruningOptionCustom) + v.Set(FlagPruningKeepRecent, 1234) + v.Set(FlagPruningKeepEvery, 4321) + v.Set(FlagPruningInterval, 10) + + return v }, expectedOptions: types.PruningOptions{ KeepRecent: 1234, @@ -38,8 +43,12 @@ func TestGetPruningOptionsFromFlags(t *testing.T) { }, }, { - name: types.PruningOptionDefault, - initParams: func() {}, + name: types.PruningOptionDefault, + initParams: func() *viper.Viper { + v := viper.New() + v.Set(FlagPruning, types.PruningOptionDefault) + return v + }, expectedOptions: types.PruneDefault, }, } @@ -50,9 +59,9 @@ func TestGetPruningOptionsFromFlags(t *testing.T) { t.Run(tt.name, func(j *testing.T) { viper.Reset() viper.SetDefault(FlagPruning, types.PruningOptionDefault) - tt.initParams() + v := tt.initParams() - opts, err := GetPruningOptionsFromFlags() + opts, err := GetPruningOptionsFromFlags(v) if tt.wantErr { require.Error(t, err) return diff --git a/server/start.go b/server/start.go index a0f6d263ab..ea7c5c62b9 100644 --- a/server/start.go +++ b/server/start.go @@ -9,7 +9,6 @@ import ( "time" "github.com/spf13/cobra" - "github.com/spf13/viper" "github.com/tendermint/tendermint/abci/server" tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" tmos "github.com/tendermint/tendermint/libs/os" @@ -20,6 +19,7 @@ import ( "github.com/tendermint/tendermint/rpc/client/local" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server/api" "github.com/cosmos/cosmos-sdk/server/config" @@ -37,6 +37,8 @@ const ( FlagHaltTime = "halt-time" FlagInterBlockCache = "inter-block-cache" FlagUnsafeSkipUpgrades = "unsafe-skip-upgrades" + FlagTrace = "trace" + FlagInvCheckPeriod = "inv-check-period" FlagPruning = "pruning" FlagPruningKeepRecent = "pruning-keep-recent" @@ -73,11 +75,12 @@ For profiling and benchmarking purposes, CPU profiling can be enabled via the '- which accepts a path for the resulting pprof file. `, PreRunE: func(cmd *cobra.Command, args []string) error { - _, err := GetPruningOptionsFromFlags() + _, err := GetPruningOptionsFromFlags(ctx.Viper) return err }, RunE: func(cmd *cobra.Command, args []string) error { - if !viper.GetBool(flagWithTendermint) { + withTM, _ := cmd.Flags().GetBool(flagWithTendermint) + if !withTM { ctx.Logger.Info("starting ABCI without Tendermint") return startStandAlone(ctx, appCreator) } @@ -89,28 +92,26 @@ which accepts a path for the resulting pprof file. }, } - // core flags for the ABCI application + cmd.Flags().String(flags.FlagHome, "", "The application home directory") cmd.Flags().Bool(flagWithTendermint, true, "Run abci app embedded in-process with tendermint") cmd.Flags().String(flagAddress, "tcp://0.0.0.0:26658", "Listen address") cmd.Flags().String(flagTraceStore, "", "Enable KVStore tracing to an output file") - cmd.Flags().String( - FlagMinGasPrices, "", - "Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 0.01photino;0.0001stake)", - ) + cmd.Flags().String(FlagMinGasPrices, "", "Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 0.01photino;0.0001stake)") cmd.Flags().IntSlice(FlagUnsafeSkipUpgrades, []int{}, "Skip a set of upgrade heights to continue the old binary") cmd.Flags().Uint64(FlagHaltHeight, 0, "Block height at which to gracefully halt the chain and shutdown the node") cmd.Flags().Uint64(FlagHaltTime, 0, "Minimum block time (in Unix seconds) at which to gracefully halt the chain and shutdown the node") cmd.Flags().Bool(FlagInterBlockCache, true, "Enable inter-block caching") cmd.Flags().String(flagCPUProfile, "", "Enable CPU profiling and write to the provided file") - + cmd.Flags().Bool(FlagTrace, false, "Provide full stack traces for errors in ABCI Log") cmd.Flags().String(FlagPruning, storetypes.PruningOptionDefault, "Pruning strategy (default|nothing|everything|custom)") cmd.Flags().Uint64(FlagPruningKeepRecent, 0, "Number of recent heights to keep on disk (ignored if pruning is not 'custom')") cmd.Flags().Uint64(FlagPruningKeepEvery, 0, "Offset heights to keep on disk after 'keep-every' (ignored if pruning is not 'custom')") cmd.Flags().Uint64(FlagPruningInterval, 0, "Height interval at which pruned heights are removed from disk (ignored if pruning is not 'custom')") - viper.BindPFlag(FlagPruning, cmd.Flags().Lookup(FlagPruning)) - viper.BindPFlag(FlagPruningKeepRecent, cmd.Flags().Lookup(FlagPruningKeepRecent)) - viper.BindPFlag(FlagPruningKeepEvery, cmd.Flags().Lookup(FlagPruningKeepEvery)) - viper.BindPFlag(FlagPruningInterval, cmd.Flags().Lookup(FlagPruningInterval)) + cmd.Flags().Uint(FlagInvCheckPeriod, 0, "Assert registered invariants every N blocks") + + // Bind flags to the Context's Viper so the app construction can set options + // accordingly. + ctx.Viper.BindPFlags(cmd.Flags()) // add support for all Tendermint-specific command line options tcmd.AddNodeFlags(cmd) @@ -118,20 +119,21 @@ which accepts a path for the resulting pprof file. } func startStandAlone(ctx *Context, appCreator AppCreator) error { - addr := viper.GetString(flagAddress) - home := viper.GetString("home") - traceWriterFile := viper.GetString(flagTraceStore) + addr := ctx.Viper.GetString(flagAddress) + home := ctx.Viper.GetString(flags.FlagHome) db, err := openDB(home) if err != nil { return err } + + traceWriterFile := ctx.Viper.GetString(flagTraceStore) traceWriter, err := openTraceWriter(traceWriterFile) if err != nil { return err } - app := appCreator(ctx.Logger, db, traceWriter) + app := appCreator(ctx.Logger, db, traceWriter, ctx.Viper) svr, err := server.NewServer(addr, "socket", app) if err != nil { @@ -146,7 +148,6 @@ func startStandAlone(ctx *Context, appCreator AppCreator) error { } tmos.TrapSignal(ctx.Logger, func() { - // cleanup err = svr.Stop() if err != nil { tmos.Exit(err.Error()) @@ -161,7 +162,7 @@ func startInProcess(ctx *Context, cdc codec.JSONMarshaler, appCreator AppCreator cfg := ctx.Config home := cfg.RootDir - traceWriterFile := viper.GetString(flagTraceStore) + traceWriterFile := ctx.Viper.GetString(flagTraceStore) db, err := openDB(home) if err != nil { return err @@ -172,7 +173,7 @@ func startInProcess(ctx *Context, cdc codec.JSONMarshaler, appCreator AppCreator return err } - app := appCreator(ctx.Logger, db, traceWriter) + app := appCreator(ctx.Logger, db, traceWriter, ctx.Viper) nodeKey, err := p2p.LoadOrGenNodeKey(cfg.NodeKeyFile()) if err != nil { @@ -180,8 +181,6 @@ func startInProcess(ctx *Context, cdc codec.JSONMarshaler, appCreator AppCreator } genDocProvider := node.DefaultGenesisDocProviderFunc(cfg) - - // create & start tendermint node tmNode, err := node.NewNode( cfg, pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()), @@ -202,16 +201,13 @@ func startInProcess(ctx *Context, cdc codec.JSONMarshaler, appCreator AppCreator var apiSrv *api.Server - config := config.GetConfig() + config := config.GetConfig(ctx.Viper) if config.API.Enable { genDoc, err := genDocProvider() if err != nil { return err } - // TODO: Since this is running in process, do we need to provide a verifier - // and set TrustNode=false? If so, we need to add additional logic that - // waits for a block to be committed first before starting the API server. clientCtx := client.Context{}. WithHomeDir(home). WithChainID(genDoc.ChainID). @@ -239,7 +235,7 @@ func startInProcess(ctx *Context, cdc codec.JSONMarshaler, appCreator AppCreator var cpuProfileCleanup func() - if cpuProfile := viper.GetString(flagCPUProfile); cpuProfile != "" { + if cpuProfile := ctx.Viper.GetString(flagCPUProfile); cpuProfile != "" { f, err := os.Create(cpuProfile) if err != nil { return err diff --git a/server/test_helpers.go b/server/test_helpers.go index dc52f0c1f9..0bc6bac801 100644 --- a/server/test_helpers.go +++ b/server/test_helpers.go @@ -2,15 +2,7 @@ package server import ( "fmt" - "io/ioutil" "net" - "os" - "testing" - - "github.com/cosmos/cosmos-sdk/client/flags" - - "github.com/spf13/viper" - "github.com/stretchr/testify/require" ) // Get a free address for a test tendermint server @@ -36,21 +28,3 @@ func FreeTCPAddr() (addr, port string, err error) { addr = fmt.Sprintf("tcp://0.0.0.0:%s", port) return } - -// SetupViper creates a homedir to run inside, -// and returns a cleanup function to defer -func SetupViper(t *testing.T) func() { - rootDir, err := ioutil.TempDir("", "mock-sdk-cmd") - require.Nil(t, err) - viper.Set(flags.FlagHome, rootDir) - viper.Set(flags.FlagName, "moniker") - return func() { - err := os.RemoveAll(rootDir) - if err != nil { - // TODO: Handle with #870 - panic(err) - } - } -} - -// DONTCOVER diff --git a/server/tm_cmds.go b/server/tm_cmds.go index 443645cdba..47db2f3256 100644 --- a/server/tm_cmds.go +++ b/server/tm_cmds.go @@ -4,9 +4,9 @@ package server import ( "fmt" + "strings" "github.com/spf13/cobra" - "github.com/spf13/viper" yaml "gopkg.in/yaml.v2" tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" @@ -51,7 +51,8 @@ func ShowValidatorCmd(ctx *Context) *cobra.Command { return err } - if viper.GetString(cli.OutputFlag) == "json" { + output, _ := cmd.Flags().GetString(cli.OutputFlag) + if strings.ToLower(output) == "json" { return printlnJSON(valPubKey) } @@ -75,13 +76,12 @@ func ShowAddressCmd(ctx *Context) *cobra.Command { Use: "show-address", Short: "Shows this node's tendermint validator consensus address", RunE: func(cmd *cobra.Command, args []string) error { - cfg := ctx.Config - privValidator := pvm.LoadOrGenFilePV( - cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()) + privValidator := pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile()) valConsAddr := (sdk.ConsAddress)(privValidator.GetAddress()) - if viper.GetString(cli.OutputFlag) == "json" { + output, _ := cmd.Flags().GetString(cli.OutputFlag) + if strings.ToLower(output) == "json" { return printlnJSON(valConsAddr) } diff --git a/server/util.go b/server/util.go index 69c8f1f415..a31eab0d31 100644 --- a/server/util.go +++ b/server/util.go @@ -2,6 +2,8 @@ package server import ( "encoding/json" + "errors" + "fmt" "net" "os" "os/signal" @@ -9,14 +11,10 @@ import ( "syscall" "time" - "errors" - "github.com/spf13/cobra" "github.com/spf13/viper" - - tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" - cfg "github.com/tendermint/tendermint/config" - "github.com/tendermint/tendermint/libs/cli" + tmcfg "github.com/tendermint/tendermint/config" + tmcli "github.com/tendermint/tendermint/libs/cli" tmflags "github.com/tendermint/tendermint/libs/cli/flags" "github.com/tendermint/tendermint/libs/log" @@ -26,105 +24,122 @@ import ( "github.com/cosmos/cosmos-sdk/version" ) +// DONTCOVER + // server context type Context struct { - Config *cfg.Config + Viper *viper.Viper + Config *tmcfg.Config Logger log.Logger } func NewDefaultContext() *Context { - return NewContext( - cfg.DefaultConfig(), - log.NewTMLogger(log.NewSyncWriter(os.Stdout)), - ) + return NewContext(viper.New(), tmcfg.DefaultConfig(), log.NewTMLogger(log.NewSyncWriter(os.Stdout))) } -func NewContext(config *cfg.Config, logger log.Logger) *Context { - return &Context{config, logger} +func NewContext(v *viper.Viper, config *tmcfg.Config, logger log.Logger) *Context { + return &Context{v, config, logger} } -//___________________________________________________________________________________ - -// PersistentPreRunEFn returns a PersistentPreRunE function for cobra -// that initailizes the passed in context with a properly configured -// logger and config object. -func PersistentPreRunEFn(context *Context) func(*cobra.Command, []string) error { +// PersistentPreRunEFn returns a PersistentPreRunE function for the root daemon +// application command. The provided context is typically the default context, +// where the logger and config are set based on the execution of parsing or +// creating a new Tendermint configuration file (config.toml). The provided +// viper object must be created at the root level and have all necessary flags, +// defined by Tendermint, bound to it. +func PersistentPreRunEFn(ctx *Context) func(*cobra.Command, []string) error { return func(cmd *cobra.Command, args []string) error { + rootViper := viper.New() + rootViper.BindPFlags(cmd.Flags()) + rootViper.BindPFlags(cmd.PersistentFlags()) + if cmd.Name() == version.Cmd.Name() { return nil } - config, err := interceptLoadConfig() + config, err := interceptConfigs(ctx, rootViper) if err != nil { return err } logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) - logger, err = tmflags.ParseLogLevel(config.LogLevel, logger, cfg.DefaultLogLevel()) + logger, err = tmflags.ParseLogLevel(config.LogLevel, logger, tmcfg.DefaultLogLevel()) if err != nil { return err } - if viper.GetBool(cli.TraceFlag) { + if rootViper.GetBool(tmcli.TraceFlag) { logger = log.NewTracingLogger(logger) } logger = logger.With("module", "main") - context.Config = config - context.Logger = logger + ctx.Config = config + ctx.Logger = logger return nil } } -// If a new config is created, change some of the default tendermint settings -func interceptLoadConfig() (conf *cfg.Config, err error) { - tmpConf := cfg.DefaultConfig() - err = viper.Unmarshal(tmpConf) - if err != nil { - // TODO: Handle with #870 - panic(err) - } - rootDir := tmpConf.RootDir - configFilePath := filepath.Join(rootDir, "config/config.toml") - // Intercept only if the file doesn't already exist +// interceptConfigs parses and updates a Tendermint configuration file or +// creates a new one and saves it. It also parses and saves the application +// configuration file. The Tendermint configuration file is parsed given a root +// Viper object, whereas the application is parsed with the private package-aware +// viperCfg object. +func interceptConfigs(ctx *Context, rootViper *viper.Viper) (*tmcfg.Config, error) { + rootDir := rootViper.GetString(flags.FlagHome) + configPath := filepath.Join(rootDir, "config") + configFile := filepath.Join(configPath, "config.toml") + + conf := tmcfg.DefaultConfig() + + if _, err := os.Stat(configFile); os.IsNotExist(err) { + tmcfg.EnsureRoot(rootDir) + + if err = conf.ValidateBasic(); err != nil { + return nil, fmt.Errorf("error in config file: %v", err) + } - if _, err := os.Stat(configFilePath); os.IsNotExist(err) { - // the following parse config is needed to create directories - conf, _ = tcmd.ParseConfig() // NOTE: ParseConfig() creates dir/files as necessary. conf.ProfListenAddress = "localhost:6060" conf.P2P.RecvRate = 5120000 conf.P2P.SendRate = 5120000 conf.TxIndex.IndexAllKeys = true conf.Consensus.TimeoutCommit = 5 * time.Second - cfg.WriteConfigFile(configFilePath, conf) - // Fall through, just so that its parsed into memory. - } + tmcfg.WriteConfigFile(configFile, conf) + } else { + rootViper.SetConfigType("toml") + rootViper.SetConfigName("config") + rootViper.AddConfigPath(configPath) + if err := rootViper.ReadInConfig(); err != nil { + return nil, fmt.Errorf("failed to read in app.toml: %w", err) + } - if conf == nil { - conf, err = tcmd.ParseConfig() // NOTE: ParseConfig() creates dir/files as necessary. - if err != nil { - panic(err) + if err := rootViper.Unmarshal(conf); err != nil { + return nil, err } } - appConfigFilePath := filepath.Join(rootDir, "config/app.toml") + appConfigFilePath := filepath.Join(configPath, "app.toml") if _, err := os.Stat(appConfigFilePath); os.IsNotExist(err) { - appConf, _ := config.ParseConfig() + appConf, err := config.ParseConfig(ctx.Viper) + if err != nil { + return nil, fmt.Errorf("failed to parse app.toml: %w", err) + } + config.WriteConfigFile(appConfigFilePath, appConf) } - viper.SetConfigName("app") - err = viper.MergeInConfig() + ctx.Viper.SetConfigType("toml") + ctx.Viper.SetConfigName("app") + ctx.Viper.AddConfigPath(configPath) + if err := ctx.Viper.ReadInConfig(); err != nil { + return nil, fmt.Errorf("failed to read in app.toml: %w", err) + } - return conf, err + return conf, nil } // add server commands -func AddCommands( - ctx *Context, cdc codec.JSONMarshaler, rootCmd *cobra.Command, appCreator AppCreator, appExport AppExporter, -) { - +func AddCommands(ctx *Context, cdc codec.JSONMarshaler, rootCmd *cobra.Command, appCreator AppCreator, appExport AppExporter) { rootCmd.PersistentFlags().String("log_level", ctx.Config.LogLevel, "Log level") tendermintCmd := &cobra.Command{ @@ -150,8 +165,6 @@ func AddCommands( ) } -//___________________________________________________________________________________ - // InsertKeyJSON inserts a new JSON field/key with a given value to an existing // JSON message. An error is returned if any serialization operation fails. // @@ -249,5 +262,3 @@ func addrToIP(addr net.Addr) net.IP { } return ip } - -// DONTCOVER diff --git a/simapp/cmd/simd/genaccounts.go b/simapp/cmd/simd/genaccounts.go index be51bfdb34..010eee8455 100644 --- a/simapp/cmd/simd/genaccounts.go +++ b/simapp/cmd/simd/genaccounts.go @@ -5,14 +5,11 @@ import ( "errors" "fmt" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/tendermint/tendermint/libs/cli" "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/server" sdk "github.com/cosmos/cosmos-sdk/types" @@ -31,10 +28,7 @@ const ( ) // AddGenesisAccountCmd returns add-genesis-account cobra Command. -func AddGenesisAccountCmd( - ctx *server.Context, depCdc codec.JSONMarshaler, cdc codec.Marshaler, defaultNodeHome, defaultClientHome string, -) *cobra.Command { - +func AddGenesisAccountCmd(ctx *server.Context, depCdc codec.JSONMarshaler, cdc codec.Marshaler, defaultClientHome string) *cobra.Command { cmd := &cobra.Command{ Use: "add-genesis-account [address_or_key_name] [coin][,[coin]]", Short: "Add a genesis account to genesis.json", @@ -46,18 +40,17 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { config := ctx.Config - config.SetRoot(viper.GetString(cli.HomeFlag)) + homeDir, _ := cmd.Flags().GetString(cli.HomeFlag) + config.SetRoot(homeDir) + + keyringBackend, _ := cmd.Flags().GetString(flags.FlagKeyringBackend) + clientHome, _ := cmd.Flags().GetString(flagClientHome) addr, err := sdk.AccAddressFromBech32(args[0]) inBuf := bufio.NewReader(cmd.InOrStdin()) if err != nil { // attempt to lookup address from Keybase if no address was provided - kb, err := keyring.New( - sdk.KeyringServiceName(), - viper.GetString(flags.FlagKeyringBackend), - viper.GetString(flagClientHome), - inBuf, - ) + kb, err := keyring.New(sdk.KeyringServiceName(), keyringBackend, clientHome, inBuf) if err != nil { return err } @@ -75,9 +68,11 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa return fmt.Errorf("failed to parse coins: %w", err) } - vestingStart := viper.GetInt64(flagVestingStart) - vestingEnd := viper.GetInt64(flagVestingEnd) - vestingAmt, err := sdk.ParseCoins(viper.GetString(flagVestingAmt)) + vestingStart, _ := cmd.Flags().GetInt64(flagVestingStart) + vestingEnd, _ := cmd.Flags().GetInt64(flagVestingEnd) + vestingAmtStr, _ := cmd.Flags().GetString(flagVestingAmt) + + vestingAmt, err := sdk.ParseCoins(vestingAmtStr) if err != nil { return fmt.Errorf("failed to parse vesting amount: %w", err) } @@ -159,12 +154,11 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa }, } - cmd.Flags().String(cli.HomeFlag, defaultNodeHome, "node's home directory") - cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)") + cmd.Flags().String(flags.FlagHome, "", "The application home directory") cmd.Flags().String(flagClientHome, defaultClientHome, "client's home directory") cmd.Flags().String(flagVestingAmt, "", "amount of coins for vesting accounts") - cmd.Flags().Uint64(flagVestingStart, 0, "schedule start time (unix epoch) for vesting accounts") - cmd.Flags().Uint64(flagVestingEnd, 0, "schedule end time (unix epoch) for vesting accounts") + cmd.Flags().Int64(flagVestingStart, 0, "schedule start time (unix epoch) for vesting accounts") + cmd.Flags().Int64(flagVestingEnd, 0, "schedule end time (unix epoch) for vesting accounts") return cmd } diff --git a/simapp/cmd/simd/main.go b/simapp/cmd/simd/main.go index 7e99919490..7ee5559051 100644 --- a/simapp/cmd/simd/main.go +++ b/simapp/cmd/simd/main.go @@ -4,6 +4,7 @@ import ( "encoding/json" "io" + "github.com/spf13/cast" "github.com/spf13/cobra" "github.com/spf13/viper" abci "github.com/tendermint/tendermint/abci/types" @@ -24,9 +25,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/staking" ) -const flagInvCheckPeriod = "inv-check-period" - -var invCheckPeriod uint +var viperCfg = viper.New() func main() { appCodec, cdc := simapp.MakeCodecs() @@ -45,6 +44,9 @@ func main() { PersistentPreRunE: server.PersistentPreRunEFn(ctx), } + rootCmd.PersistentFlags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)") + viperCfg.BindPFlags(rootCmd.Flags()) + rootCmd.AddCommand( genutilcli.InitCmd(ctx, cdc, simapp.ModuleBasics, simapp.DefaultNodeHome), genutilcli.CollectGenTxsCmd(ctx, cdc, banktypes.GenesisBalancesIterator{}, simapp.DefaultNodeHome), @@ -54,49 +56,47 @@ func main() { banktypes.GenesisBalancesIterator{}, simapp.DefaultNodeHome, simapp.DefaultCLIHome, ), genutilcli.ValidateGenesisCmd(ctx, cdc, simapp.ModuleBasics), - AddGenesisAccountCmd(ctx, cdc, appCodec, simapp.DefaultNodeHome, simapp.DefaultCLIHome), + AddGenesisAccountCmd(ctx, cdc, appCodec, simapp.DefaultCLIHome), flags.NewCompletionCmd(rootCmd, true), testnetCmd(ctx, cdc, simapp.ModuleBasics, banktypes.GenesisBalancesIterator{}), - debug.Cmd(cdc)) + debug.Cmd(cdc), + ) server.AddCommands(ctx, cdc, rootCmd, newApp, exportAppStateAndTMValidators) - // prepare and add flags - executor := cli.PrepareBaseCmd(rootCmd, "GA", simapp.DefaultNodeHome) - rootCmd.PersistentFlags().UintVar(&invCheckPeriod, flagInvCheckPeriod, - 0, "Assert registered invariants every N blocks") - err := executor.Execute() - if err != nil { + executor := cli.PrepareBaseCmd(rootCmd, "", simapp.DefaultNodeHome) + if err := executor.Execute(); err != nil { panic(err) } } -func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) server.Application { +func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts server.AppOptions) server.Application { var cache sdk.MultiStorePersistentCache - if viper.GetBool(server.FlagInterBlockCache) { + if cast.ToBool(appOpts.Get(server.FlagInterBlockCache)) { cache = store.NewCommitKVStoreCacheManager() } skipUpgradeHeights := make(map[int64]bool) - for _, h := range viper.GetIntSlice(server.FlagUnsafeSkipUpgrades) { + for _, h := range cast.ToIntSlice(appOpts.Get(server.FlagUnsafeSkipUpgrades)) { skipUpgradeHeights[int64(h)] = true } - pruningOpts, err := server.GetPruningOptionsFromFlags() + pruningOpts, err := server.GetPruningOptionsFromFlags(appOpts) if err != nil { panic(err) } - // TODO: Make sure custom pruning works. return simapp.NewSimApp( logger, db, traceStore, true, skipUpgradeHeights, - viper.GetString(flags.FlagHome), invCheckPeriod, + cast.ToString(appOpts.Get(flags.FlagHome)), + cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)), baseapp.SetPruning(pruningOpts), - baseapp.SetMinGasPrices(viper.GetString(server.FlagMinGasPrices)), - baseapp.SetHaltHeight(viper.GetUint64(server.FlagHaltHeight)), - baseapp.SetHaltTime(viper.GetUint64(server.FlagHaltTime)), + 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.SetInterBlockCache(cache), + baseapp.SetTrace(cast.ToBool(appOpts.Get(server.FlagTrace))), ) } @@ -107,12 +107,13 @@ func exportAppStateAndTMValidators( var simApp *simapp.SimApp if height != -1 { simApp = simapp.NewSimApp(logger, db, traceStore, false, map[int64]bool{}, "", uint(1)) - err := simApp.LoadHeight(height) - if err != nil { + + if err := simApp.LoadHeight(height); err != nil { return nil, nil, nil, err } } else { simApp = simapp.NewSimApp(logger, db, traceStore, true, map[int64]bool{}, "", uint(1)) } + return simApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) } diff --git a/simapp/cmd/simd/testnet.go b/simapp/cmd/simd/testnet.go index 09b1feca29..8a3eca2591 100644 --- a/simapp/cmd/simd/testnet.go +++ b/simapp/cmd/simd/testnet.go @@ -11,7 +11,6 @@ import ( "path/filepath" "github.com/spf13/cobra" - "github.com/spf13/viper" tmconfig "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto" tmos "github.com/tendermint/tendermint/libs/os" @@ -62,39 +61,31 @@ Example: RunE: func(cmd *cobra.Command, _ []string) error { config := ctx.Config - outputDir := viper.GetString(flagOutputDir) - chainID := viper.GetString(flags.FlagChainID) - minGasPrices := viper.GetString(server.FlagMinGasPrices) - nodeDirPrefix := viper.GetString(flagNodeDirPrefix) - nodeDaemonHome := viper.GetString(flagNodeDaemonHome) - nodeCLIHome := viper.GetString(flagNodeCLIHome) - startingIPAddress := viper.GetString(flagStartingIPAddress) - numValidators := viper.GetInt(flagNumValidators) + outputDir, _ := cmd.Flags().GetString(flagOutputDir) + keyringBackend, _ := cmd.Flags().GetString(flags.FlagKeyringBackend) + chainID, _ := cmd.Flags().GetString(flags.FlagChainID) + minGasPrices, _ := cmd.Flags().GetString(server.FlagMinGasPrices) + nodeDirPrefix, _ := cmd.Flags().GetString(flagNodeDirPrefix) + nodeDaemonHome, _ := cmd.Flags().GetString(flagNodeDaemonHome) + nodeCLIHome, _ := cmd.Flags().GetString(flagNodeCLIHome) + startingIPAddress, _ := cmd.Flags().GetString(flagStartingIPAddress) + numValidators, _ := cmd.Flags().GetInt(flagNumValidators) return InitTestnet( cmd, config, cdc, mbm, genBalIterator, outputDir, chainID, minGasPrices, - nodeDirPrefix, nodeDaemonHome, nodeCLIHome, startingIPAddress, numValidators, + nodeDirPrefix, nodeDaemonHome, nodeCLIHome, startingIPAddress, keyringBackend, numValidators, ) }, } - cmd.Flags().Int(flagNumValidators, 4, - "Number of validators to initialize the testnet with") - cmd.Flags().StringP(flagOutputDir, "o", "./mytestnet", - "Directory to store initialization data for the testnet") - cmd.Flags().String(flagNodeDirPrefix, "node", - "Prefix the directory name for each node with (node results in node0, node1, ...)") - cmd.Flags().String(flagNodeDaemonHome, "simd", - "Home directory of the node's daemon configuration") - cmd.Flags().String(flagNodeCLIHome, "simcli", - "Home directory of the node's cli configuration") - cmd.Flags().String(flagStartingIPAddress, "192.168.0.1", - "Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)") - cmd.Flags().String( - flags.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") - cmd.Flags().String( - server.FlagMinGasPrices, fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom), - "Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.01photino,0.001stake)") + cmd.Flags().Int(flagNumValidators, 4, "Number of validators to initialize the testnet with") + cmd.Flags().StringP(flagOutputDir, "o", "./mytestnet", "Directory to store initialization data for the testnet") + cmd.Flags().String(flagNodeDirPrefix, "node", "Prefix the directory name for each node with (node results in node0, node1, ...)") + cmd.Flags().String(flagNodeDaemonHome, "simd", "Home directory of the node's daemon configuration") + cmd.Flags().String(flagNodeCLIHome, "simcli", "Home directory of the node's cli configuration") + cmd.Flags().String(flagStartingIPAddress, "192.168.0.1", "Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)") + cmd.Flags().String(flags.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") + cmd.Flags().String(server.FlagMinGasPrices, fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom), "Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.01photino,0.001stake)") cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)") return cmd @@ -107,7 +98,7 @@ func InitTestnet( cmd *cobra.Command, config *tmconfig.Config, cdc codec.JSONMarshaler, mbm module.BasicManager, genBalIterator banktypes.GenesisBalancesIterator, outputDir, chainID, minGasPrices, nodeDirPrefix, nodeDaemonHome, - nodeCLIHome, startingIPAddress string, numValidators int, + nodeCLIHome, startingIPAddress, keyringBackend string, numValidators int, ) error { if chainID == "" { @@ -171,12 +162,7 @@ func InitTestnet( memo := fmt.Sprintf("%s@%s:26656", nodeIDs[i], ip) genFiles = append(genFiles, config.GenesisFile()) - kb, err := keyring.New( - sdk.KeyringServiceName(), - viper.GetString(flags.FlagKeyringBackend), - clientDir, - inBuf, - ) + kb, err := keyring.New(sdk.KeyringServiceName(), keyringBackend, clientDir, inBuf) if err != nil { return err } diff --git a/types/errors/abci.go b/types/errors/abci.go index 04ba8413d3..f297e4ca69 100644 --- a/types/errors/abci.go +++ b/types/errors/abci.go @@ -1,7 +1,6 @@ package errors import ( - "errors" "fmt" "reflect" @@ -18,8 +17,6 @@ const ( // detailed error string. internalABCICodespace = UndefinedCodespace internalABCICode uint32 = 1 - internalABCILog string = "internal error" - // multiErrorABCICode uint32 = 1000 ) // ABCIInfo returns the ABCI error information as consumed by the tendermint @@ -44,8 +41,8 @@ func ABCIInfo(err error, debug bool) (codespace string, code uint32, log string) // ResponseCheckTx returns an ABCI ResponseCheckTx object with fields filled in // from the given error and gas values. -func ResponseCheckTx(err error, gw, gu uint64) abci.ResponseCheckTx { - space, code, log := ABCIInfo(err, false) +func ResponseCheckTx(err error, gw, gu uint64, debug bool) abci.ResponseCheckTx { + space, code, log := ABCIInfo(err, debug) return abci.ResponseCheckTx{ Codespace: space, Code: code, @@ -57,8 +54,8 @@ func ResponseCheckTx(err error, gw, gu uint64) abci.ResponseCheckTx { // ResponseDeliverTx returns an ABCI ResponseDeliverTx object with fields filled in // from the given error and gas values. -func ResponseDeliverTx(err error, gw, gu uint64) abci.ResponseDeliverTx { - space, code, log := ABCIInfo(err, false) +func ResponseDeliverTx(err error, gw, gu uint64, debug bool) abci.ResponseDeliverTx { + space, code, log := ABCIInfo(err, debug) return abci.ResponseDeliverTx{ Codespace: space, Code: code, @@ -159,10 +156,11 @@ func errIsNil(err error) bool { // simply returned. func Redact(err error) error { if ErrPanic.Is(err) { - return errors.New(internalABCILog) + return ErrPanic } if abciCode(err) == internalABCICode { - return errors.New(internalABCILog) + return errInternal } + return err } diff --git a/types/errors/abci_test.go b/types/errors/abci_test.go index 6e89d83cdd..39c1a0fd6f 100644 --- a/types/errors/abci_test.go +++ b/types/errors/abci_test.go @@ -7,7 +7,6 @@ import ( "testing" "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" ) func TestABCInfo(t *testing.T) { @@ -49,7 +48,7 @@ func TestABCInfo(t *testing.T) { "stdlib is generic message": { err: io.EOF, debug: false, - wantLog: "internal error", + wantLog: "internal", wantCode: 1, wantSpace: UndefinedCodespace, }, @@ -63,7 +62,7 @@ func TestABCInfo(t *testing.T) { "wrapped stdlib is only a generic message": { err: Wrap(io.EOF, "cannot read file"), debug: false, - wantLog: "internal error", + wantLog: "internal", wantCode: 1, wantSpace: UndefinedCodespace, }, @@ -95,9 +94,15 @@ func TestABCInfo(t *testing.T) { tc := tc t.Run(testName, func(t *testing.T) { space, code, log := ABCIInfo(tc.err, tc.debug) - require.Equal(t, tc.wantSpace, space) - require.Equal(t, tc.wantCode, code) - require.Equal(t, tc.wantLog, log) + if space != tc.wantSpace { + t.Errorf("want %s space, got %s", tc.wantSpace, space) + } + if code != tc.wantCode { + t.Errorf("want %d code, got %d", tc.wantCode, code) + } + if log != tc.wantLog { + t.Errorf("want %q log, got %q", tc.wantLog, log) + } }) } } @@ -131,7 +136,7 @@ func TestABCIInfoStacktrace(t *testing.T) { err: Wrap(fmt.Errorf("stdlib"), "wrapped"), debug: false, wantStacktrace: false, - wantErrMsg: "internal error", + wantErrMsg: "internal", }, } @@ -159,28 +164,50 @@ func TestABCIInfoStacktrace(t *testing.T) { func TestABCIInfoHidesStacktrace(t *testing.T) { err := Wrap(ErrUnauthorized, "wrapped") _, _, log := ABCIInfo(err, false) - require.Equal(t, "wrapped: unauthorized", log) + + if log != "wrapped: unauthorized" { + t.Fatalf("unexpected message in non debug mode: %s", log) + } } func TestRedact(t *testing.T) { - if err := Redact(ErrPanic); ErrPanic.Is(err) { - t.Error("reduct must not pass through panic error") - } - if err := Redact(ErrUnauthorized); !ErrUnauthorized.Is(err) { - t.Error("reduct should pass through SDK error") + cases := map[string]struct { + err error + untouched bool // if true we expect the same error after redact + changed error // if untouched == false, expect this error + }{ + "panic looses message": { + err: Wrap(ErrPanic, "some secret stack trace"), + changed: ErrPanic, + }, + "sdk errors untouched": { + err: Wrap(ErrUnauthorized, "cannot drop db"), + untouched: true, + }, + "pass though custom errors with ABCI code": { + err: customErr{}, + untouched: true, + }, + "redact stdlib error": { + err: fmt.Errorf("stdlib error"), + changed: errInternal, + }, } - var cerr customErr - if err := Redact(cerr); err != cerr { - t.Error("reduct should pass through ABCI code error") + for name, tc := range cases { + spec := tc + t.Run(name, func(t *testing.T) { + redacted := Redact(spec.err) + if spec.untouched { + require.Equal(t, spec.err, redacted) + } else { + // see if we got the expected redact + require.Equal(t, spec.changed, redacted) + // make sure the ABCI code did not change + require.Equal(t, abciCode(spec.err), abciCode(redacted)) + } + }) } - - serr := fmt.Errorf("stdlib error") - if err := Redact(serr); err == serr { - t.Error("reduct must not pass through a stdlib error") - } - - require.Nil(t, Redact(nil)) } func TestABCIInfoSerializeErr(t *testing.T) { @@ -211,47 +238,23 @@ func TestABCIInfoSerializeErr(t *testing.T) { debug: true, exp: fmt.Sprintf("%+v", myErrDecode), }, - // "multi error default encoder": { - // src: Append(myErrMsg, myErrAddr), - // exp: Append(myErrMsg, myErrAddr).Error(), - // }, - // "multi error default with internal": { - // src: Append(myErrMsg, myPanic), - // exp: "internal error", - // }, "redact in default encoder": { src: myPanic, - exp: "internal error", + exp: "panic", }, "do not redact in debug encoder": { src: myPanic, debug: true, exp: fmt.Sprintf("%+v", myPanic), }, - // "redact in multi error": { - // src: Append(myPanic, myErrMsg), - // debug: false, - // exp: "internal error", - // }, - // "no redact in multi error": { - // src: Append(myPanic, myErrMsg), - // debug: true, - // exp: `2 errors occurred: - // * panic - // * test: invalid message - // `, - // }, - // "wrapped multi error with redact": { - // src: Wrap(Append(myPanic, myErrMsg), "wrap"), - // debug: false, - // exp: "internal error", - // }, } for msg, spec := range specs { spec := spec t.Run(msg, func(t *testing.T) { _, _, log := ABCIInfo(spec.src, spec.debug) - require.Equal(t, spec.exp, log) + if exp, got := spec.exp, log; exp != got { + t.Errorf("expected %v but got %v", exp, got) + } }) } } @@ -265,30 +268,3 @@ func (customErr) Codespace() string { return "extern" } func (customErr) ABCICode() uint32 { return 999 } func (customErr) Error() string { return "custom" } - -func TestResponseCheckDeliverTx(t *testing.T) { - t.Parallel() - require.Equal(t, abci.ResponseCheckTx{ - Codespace: "extern", - Code: 999, - Log: "custom", - GasWanted: int64(1), - GasUsed: int64(2), - }, ResponseCheckTx(customErr{}, 1, 2)) - require.Equal(t, abci.ResponseDeliverTx{ - Codespace: "extern", - Code: 999, - Log: "custom", - GasWanted: int64(1), - GasUsed: int64(2), - }, ResponseDeliverTx(customErr{}, 1, 2)) -} - -func TestQueryResult(t *testing.T) { - t.Parallel() - require.Equal(t, abci.ResponseQuery{ - Codespace: "extern", - Code: 999, - Log: "custom", - }, QueryResult(customErr{})) -} diff --git a/x/genutil/client/cli/collect.go b/x/genutil/client/cli/collect.go index 01c9f66914..033966f979 100644 --- a/x/genutil/client/cli/collect.go +++ b/x/genutil/client/cli/collect.go @@ -27,6 +27,7 @@ func CollectGenTxsCmd(ctx *server.Context, cdc codec.JSONMarshaler, genBalIterat RunE: func(_ *cobra.Command, _ []string) error { config := ctx.Config config.SetRoot(viper.GetString(cli.HomeFlag)) + name := viper.GetString(flags.FlagName) nodeID, valPubKey, err := genutil.InitializeNodeValidatorFiles(config) if err != nil { @@ -59,9 +60,7 @@ func CollectGenTxsCmd(ctx *server.Context, cdc codec.JSONMarshaler, genBalIterat } cmd.Flags().String(cli.HomeFlag, defaultNodeHome, "node's home directory") - cmd.Flags().String(flagGenTxDir, "", - "override default \"gentx\" directory from which collect and execute "+ - "genesis transactions; default [--home]/config/gentx/") + cmd.Flags().String(flagGenTxDir, "", "override default \"gentx\" directory from which collect and execute genesis transactions; default [--home]/config/gentx/") return cmd } diff --git a/x/genutil/client/cli/init_test.go b/x/genutil/client/cli/init_test.go index 3fb52fc7fb..54fa82ca15 100644 --- a/x/genutil/client/cli/init_test.go +++ b/x/genutil/client/cli/init_test.go @@ -28,8 +28,7 @@ import ( var testMbm = module.NewBasicManager(genutil.AppModuleBasic{}) func TestInitCmd(t *testing.T) { - t.Cleanup(server.SetupViper(t)) - t.Cleanup(server.SetupViper(t)) + t.SkipNow() home, cleanup := tests.NewTestCaseDir(t) t.Cleanup(cleanup) @@ -37,7 +36,7 @@ func TestInitCmd(t *testing.T) { cfg, err := tcmd.ParseConfig() require.Nil(t, err) - ctx := server.NewContext(cfg, logger) + ctx := server.NewContext(viper.New(), cfg, logger) cdc := makeCodec() cmd := InitCmd(ctx, cdc, testMbm, home) @@ -51,7 +50,7 @@ func setupClientHome(t *testing.T) func() { } func TestEmptyState(t *testing.T) { - t.Cleanup(server.SetupViper(t)) + t.SkipNow() t.Cleanup(setupClientHome(t)) home, cleanup := tests.NewTestCaseDir(t) @@ -61,7 +60,7 @@ func TestEmptyState(t *testing.T) { cfg, err := tcmd.ParseConfig() require.Nil(t, err) - ctx := server.NewContext(cfg, logger) + ctx := server.NewContext(viper.New(), cfg, logger) cdc := makeCodec() cmd := InitCmd(ctx, cdc, testMbm, home) @@ -102,7 +101,7 @@ func TestStartStandAlone(t *testing.T) { logger := log.NewNopLogger() cfg, err := tcmd.ParseConfig() require.Nil(t, err) - ctx := server.NewContext(cfg, logger) + ctx := server.NewContext(viper.New(), cfg, logger) cdc := makeCodec() initCmd := InitCmd(ctx, cdc, testMbm, home) require.NoError(t, initCmd.RunE(nil, []string{"appnode-test"})) diff --git a/x/genutil/client/cli/migrate_test.go b/x/genutil/client/cli/migrate_test.go index d7ea942500..a974a69f9a 100644 --- a/x/genutil/client/cli/migrate_test.go +++ b/x/genutil/client/cli/migrate_test.go @@ -44,7 +44,7 @@ func TestMigrateGenesis(t *testing.T) { logger := log.NewNopLogger() cfg, err := tcmd.ParseConfig() require.Nil(t, err) - ctx := server.NewContext(cfg, logger) + ctx := server.NewContext(viper.New(), cfg, logger) cdc := makeCodec() genesisPath := path.Join(home, "genesis.json") diff --git a/x/staking/client/cli/tx_test.go b/x/staking/client/cli/tx_test.go index 5759c38970..07b9e4aab5 100644 --- a/x/staking/client/cli/tx_test.go +++ b/x/staking/client/cli/tx_test.go @@ -15,11 +15,11 @@ import ( ) func TestPrepareFlagsForTxCreateValidator(t *testing.T) { - defer server.SetupViper(t)() + t.SkipNow() config, err := tcmd.ParseConfig() require.Nil(t, err) logger := log.NewNopLogger() - ctx := server.NewContext(config, logger) + ctx := server.NewContext(viper.New(), config, logger) valPubKey, _ := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeConsPub, "cosmosvalconspub1zcjduepq7jsrkl9fgqk0wj3ahmfr8pgxj6vakj2wzn656s8pehh0zhv2w5as5gd80a")