Repository: ovrclk/akash
Pull request #896 (2020-11-06 09:59:16)

fix app (master): prevent double init

codecov[bot]: # [Codecov](https://codecov.io/gh/ovrclk/akash/pull/896?src=pr&el=h1) Report
> Merging [#896](https://codecov.io/gh/ovrclk/akash/pull/896?src=pr&el=desc) into [master](https://codecov.io/gh/ovrclk/akash/commit/c0becaf676e20fd807a125776b55c3c90968c19a?el=desc) will **decrease** coverage by `0.00%`.
> The diff coverage is `35.71%`.

```diff
@@            Coverage Diff             @@
##           master     #896      +/-   ##
==========================================
- Coverage   39.78%   39.77%   -0.01%     
==========================================
  Files         175      175              
  Lines        8986     8987       +1     
==========================================
  Hits         3575     3575              
- Misses       4997     4998       +1     
  Partials      414      414              
```



Patch:
diff --git a/app/app.go b/app/app.go
index 1d2cd27d..db714fe5 100644
--- a/app/app.go
+++ b/app/app.go
@@ -141,7 +141,7 @@ type AkashApp struct {
 
 // NewApp creates and returns a new Akash App.
 func NewApp(
-	logger log.Logger, db dbm.DB, tio io.Writer, invCheckPeriod uint, skipUpgradeHeights map[int64]bool,
+	logger log.Logger, db dbm.DB, tio io.Writer, loadLatest bool, invCheckPeriod uint, skipUpgradeHeights map[int64]bool,
 	homePath string, options ...func(*bam.BaseApp),
 ) *AkashApp {
 
@@ -397,15 +397,22 @@ func NewApp(
 
 	app.SetEndBlocker(app.EndBlocker)
 
-	// TODO: add load latest check here
-	err := app.LoadLatestVersion()
-	if err != nil {
-		tmos.Exit("app initialization:" + err.Error())
+	if loadLatest {
+		if err := app.LoadLatestVersion(); err != nil {
+			tmos.Exit("app initialization:" + err.Error())
+		}
+
+		// Initialize and seal the capability keeper so all persistent capabilities
+		// are loaded in-memory and prevent any further modules from creating scoped
+		// sub-keepers.
+		// This must be done during creation of baseapp rather than in InitChain so
+		// that in-memory capabilities get regenerated on app restart.
+		// Note that since this reads from the store, we can only perform it when
+		// `loadLatest` is set to true.
+		ctx := app.BaseApp.NewUncachedContext(true, tmproto.Header{})
+		app.keeper.cap.InitializeAndSeal(ctx)
 	}
 
-	ctx := app.BaseApp.NewUncachedContext(true, tmproto.Header{})
-	app.keeper.cap.InitializeAndSeal(ctx)
-
 	app.keeper.scopedIBC = scopedIBCKeeper
 	app.keeper.scopedTransfer = scopedTransferKeeper
 
diff --git a/app/export.go b/app/export.go
index b5e8c6fc..292627d6 100644
--- a/app/export.go
+++ b/app/export.go
@@ -199,7 +199,7 @@ func (app *AkashApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs
 // Setup initializes a new AkashApp. A Nop logger is set in AkashApp.
 func Setup(isCheckTx bool) *AkashApp {
 	db := dbm.NewMemDB()
-	app := NewApp(logger.NewNopLogger(), db, nil, 5, map[int64]bool{}, DefaultHome)
+	app := NewApp(logger.NewNopLogger(), db, nil, true, 5, map[int64]bool{}, DefaultHome)
 	if !isCheckTx {
 		// init chain must be called to stop deliverState from being nil
 		genesisState := NewDefaultGenesisState()
diff --git a/cmd/akash/cmd/root.go b/cmd/akash/cmd/root.go
index f60ff48c..56b9d25e 100644
--- a/cmd/akash/cmd/root.go
+++ b/cmd/akash/cmd/root.go
@@ -3,7 +3,6 @@ package cmd
 import (
 	"context"
 	"io"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 
@@ -138,7 +137,7 @@ func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts serverty
 	}
 
 	return app.NewApp(
-		logger, db, traceStore, cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)), skipUpgradeHeights,
+		logger, db, traceStore, true, cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)), skipUpgradeHeights,
 		cast.ToString(appOpts.Get(flags.FlagHome)),
 		baseapp.SetPruning(pruningOpts),
 		baseapp.SetMinGasPrices(cast.ToString(appOpts.Get(server.FlagMinGasPrices))),
@@ -158,17 +157,18 @@ func createAppAndExport(
 	logger log.Logger, db dbm.DB, tio io.Writer, height int64, forZeroHeight bool, jailAllowedAddrs []string,
 ) (servertypes.ExportedApp, error) {
 
-	app := app.NewApp(logger, db, ioutil.Discard, uint(1), map[int64]bool{}, "")
-
+	var akashApp *app.AkashApp
 	if height != -1 {
-		err := app.LoadHeight(height)
-		if err != nil {
+		akashApp = app.NewApp(logger, db, tio, false, uint(1), map[int64]bool{}, "")
+
+		if err := akashApp.LoadHeight(height); err != nil {
 			return servertypes.ExportedApp{}, err
 		}
-		return app.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs)
+	} else {
+		akashApp = app.NewApp(logger, db, tio, true, uint(1), map[int64]bool{}, "")
 	}
 
-	return app.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs)
+	return akashApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs)
 }
 
 func queryCmd() *cobra.Command {


Test patch:
diff --git a/app/app_test.go b/app/app_test.go
index f2fdbf30..1f9b6353 100644
--- a/app/app_test.go
+++ b/app/app_test.go
@@ -16,7 +16,7 @@ import (
 func TestAppExport(t *testing.T) {
 	db := dbm.NewMemDB()
 	app1 := app.NewApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)),
-		db, nil, 0, map[int64]bool{}, app.DefaultHome)
+		db, nil, true, 0, map[int64]bool{}, app.DefaultHome)
 
 	genesisState := app.NewDefaultGenesisState()
 	stateBytes, err := json.MarshalIndent(genesisState, "", "  ")
@@ -32,7 +32,7 @@ func TestAppExport(t *testing.T) {
 	app1.Commit()
 
 	// Making a new app object with the db, so that initchain hasn't been called
-	app2 := app.NewApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, 0, map[int64]bool{}, app.DefaultHome)
+	app2 := app.NewApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, 0, map[int64]bool{}, app.DefaultHome)
 	_, err = app2.ExportAppStateAndValidators(false, []string{})
 	require.NoError(t, err, "ExportAppStateAndValidators should not have an error")
 }
diff --git a/app/sim_test.go b/app/sim_test.go
index 46ac75a8..c96c699e 100644
--- a/app/sim_test.go
+++ b/app/sim_test.go
@@ -82,7 +82,7 @@ func TestFullAppSimulation(t *testing.T) {
 		require.NoError(t, os.RemoveAll(dir))
 	}()
 
-	app1 := NewApp(logger, db, nil, simapp.FlagPeriodValue, map[int64]bool{}, DefaultHome, fauxMerkleModeOpt)
+	app1 := NewApp(logger, db, nil, true, simapp.FlagPeriodValue, map[int64]bool{}, DefaultHome, fauxMerkleModeOpt)
 	require.Equal(t, "akash", app1.Name())
 
 	fmt.Printf("config-------- %v", config)
@@ -111,7 +111,7 @@ func TestAppImportExport(t *testing.T) {
 		require.NoError(t, os.RemoveAll(dir))
 	}()
 
-	app := NewApp(logger, db, nil, simapp.FlagPeriodValue, map[int64]bool{}, DefaultHome, fauxMerkleModeOpt)
+	app := NewApp(logger, db, nil, true, simapp.FlagPeriodValue, map[int64]bool{}, DefaultHome, fauxMerkleModeOpt)
 	require.Equal(t, AppName, app.Name())
 
 	// Run randomized simulation
@@ -141,7 +141,7 @@ func TestAppImportExport(t *testing.T) {
 		require.NoError(t, os.RemoveAll(newDir))
 	}()
 
-	newApp := NewApp(log.NewNopLogger(), newDB, nil, simapp.FlagPeriodValue, map[int64]bool{}, DefaultHome, fauxMerkleModeOpt)
+	newApp := NewApp(log.NewNopLogger(), newDB, nil, true, simapp.FlagPeriodValue, map[int64]bool{}, DefaultHome, fauxMerkleModeOpt)
 	require.Equal(t, AppName, newApp.Name())
 
 	var genesisState simapp.GenesisState
@@ -201,7 +201,7 @@ func TestAppSimulationAfterImport(t *testing.T) {
 		require.NoError(t, os.RemoveAll(dir))
 	}()
 
-	app := NewApp(logger, db, nil, simapp.FlagPeriodValue, map[int64]bool{}, DefaultHome, fauxMerkleModeOpt)
+	app := NewApp(logger, db, nil, true, simapp.FlagPeriodValue, map[int64]bool{}, DefaultHome, fauxMerkleModeOpt)
 	require.Equal(t, AppName, app.Name())
 
 	// Run randomized simulation
@@ -236,7 +236,7 @@ func TestAppSimulationAfterImport(t *testing.T) {
 		require.NoError(t, os.RemoveAll(newDir))
 	}()
 
-	newApp := NewApp(log.NewNopLogger(), newDB, nil, simapp.FlagPeriodValue, map[int64]bool{}, DefaultHome, fauxMerkleModeOpt)
+	newApp := NewApp(log.NewNopLogger(), newDB, nil, true, simapp.FlagPeriodValue, map[int64]bool{}, DefaultHome, fauxMerkleModeOpt)
 	require.Equal(t, AppName, newApp.Name())
 
 	newApp.InitChain(abci.RequestInitChain{
@@ -276,7 +276,7 @@ func TestAppStateDeterminism(t *testing.T) {
 
 			db := dbm.NewMemDB()
 
-			app := NewApp(logger, db, nil, simapp.FlagPeriodValue, map[int64]bool{}, DefaultHome, interBlockCacheOpt())
+			app := NewApp(logger, db, nil, true, simapp.FlagPeriodValue, map[int64]bool{}, DefaultHome, interBlockCacheOpt())
 
 			fmt.Printf(
 				"running non-determinism simulation; seed %d: %d/%d, attempt: %d/%d\n",
diff --git a/testutil/types.go b/testutil/types.go
index 1c17b90c..35e303cc 100644
--- a/testutil/types.go
+++ b/testutil/types.go
@@ -50,7 +50,7 @@ func ResourceUnits(_ testing.TB) types.ResourceUnits {
 
 func NewApp(val network.Validator) servertypes.Application {
 	return app.NewApp(
-		val.Ctx.Logger, dbm.NewMemDB(), nil, 0, make(map[int64]bool), val.Ctx.Config.RootDir,
+		val.Ctx.Logger, dbm.NewMemDB(), nil, true, 0, make(map[int64]bool), val.Ctx.Config.RootDir,
 		baseapp.SetPruning(storetypes.NewPruningOptionsFromString(val.AppConfig.Pruning)),
 		baseapp.SetMinGasPrices(val.AppConfig.MinGasPrices),
 	)