From 9db2f8b8bffe3dd1dd4eaf7d9b1d38afb71a193a Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Wed, 17 Apr 2024 19:09:10 +0200 Subject: [PATCH] feat!: use versioned keys for dynamically adding and remove commit stores (#3320) As was discovered with the apphash mismatch in https://github.com/celestiaorg/celestia-app/issues/3167, by adding both v1 and v2 stores in the constructor we actually end up with a different app hash to `v1.x` as now there are a bunch of empy but initialized stores. What's needed is a mechanism that can start with only the v1 stores and then during the upgrade process add or remove the store for v2 as necessary. The main problem with this is that because we are using a branch of state we can't actually modify the stores until we write the new state to disk. Hence we can no longer perform the migration in `EndBlock` but in `Commit` whereby we first write state, then create the new stores, then perform the module migrations (like call `InitGenesis`) and then write it again finally returning the app hash. This PR depends on https://github.com/celestiaorg/cosmos-sdk/pull/387 which introduces the upgrade hooks during `Commit`. This PR itself adds the two hooks for updating the stores and performing the migrations. --------- Co-authored-by: Rootul Patel --- app/app.go | 462 ++++++------------ app/app_test.go | 3 +- app/module/module.go | 31 +- app/modules.go | 373 ++++++++++++++ app/test/ica_upgrade_test.go | 54 +- app/test/upgrade_test.go | 3 +- cmd/celestia-appd/cmd/root.go | 6 +- go.mod | 2 +- go.sum | 4 +- .../packetforward_upgrade_test.go | 50 +- test/testground/go.mod | 2 +- test/testground/go.sum | 4 +- test/tokenfilter/setup.go | 2 +- test/util/malicious/app.go | 3 +- test/util/malicious/test_app.go | 2 +- test/util/test_app.go | 51 +- x/minfee/upgrade_test.go | 2 +- 17 files changed, 612 insertions(+), 442 deletions(-) create mode 100644 app/modules.go diff --git a/app/app.go b/app/app.go index d3f0544c5f..10b232fa94 100644 --- a/app/app.go +++ b/app/app.go @@ -1,12 +1,13 @@ package app import ( + "fmt" "io" + "slices" "github.com/celestiaorg/celestia-app/v2/app/module" "github.com/celestiaorg/celestia-app/v2/app/posthandler" "github.com/celestiaorg/celestia-app/v2/x/minfee" - "github.com/celestiaorg/celestia-app/v2/x/mint" mintkeeper "github.com/celestiaorg/celestia-app/v2/x/mint/keeper" minttypes "github.com/celestiaorg/celestia-app/v2/x/mint/types" "github.com/cosmos/cosmos-sdk/baseapp" @@ -22,19 +23,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkmodule "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/version" - "github.com/cosmos/cosmos-sdk/x/auth" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/cosmos/cosmos-sdk/x/auth/vesting" - vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" - "github.com/cosmos/cosmos-sdk/x/authz" authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" - authzmodule "github.com/cosmos/cosmos-sdk/x/authz/module" - "github.com/cosmos/cosmos-sdk/x/bank" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/cosmos/cosmos-sdk/x/capability" capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" "github.com/cosmos/cosmos-sdk/x/crisis" @@ -43,27 +37,19 @@ import ( distr "github.com/cosmos/cosmos-sdk/x/distribution" distrkeeper "github.com/cosmos/cosmos-sdk/x/distribution/keeper" distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" - "github.com/cosmos/cosmos-sdk/x/evidence" evidencekeeper "github.com/cosmos/cosmos-sdk/x/evidence/keeper" evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" "github.com/cosmos/cosmos-sdk/x/feegrant" feegrantkeeper "github.com/cosmos/cosmos-sdk/x/feegrant/keeper" - feegrantmodule "github.com/cosmos/cosmos-sdk/x/feegrant/module" - "github.com/cosmos/cosmos-sdk/x/genutil" - genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - "github.com/cosmos/cosmos-sdk/x/gov" govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" govv1beta2 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" oldgovtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" - "github.com/cosmos/cosmos-sdk/x/params" paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" paramproposal "github.com/cosmos/cosmos-sdk/x/params/types/proposal" - "github.com/cosmos/cosmos-sdk/x/slashing" slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" - "github.com/cosmos/cosmos-sdk/x/staking" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" @@ -71,7 +57,6 @@ import ( "github.com/cosmos/ibc-go/v6/modules/apps/transfer" ibctransferkeeper "github.com/cosmos/ibc-go/v6/modules/apps/transfer/keeper" ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" - ibc "github.com/cosmos/ibc-go/v6/modules/core" ibcclienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" ibcporttypes "github.com/cosmos/ibc-go/v6/modules/core/05-port/types" ibchost "github.com/cosmos/ibc-go/v6/modules/core/24-host" @@ -88,13 +73,11 @@ import ( appv1 "github.com/celestiaorg/celestia-app/v2/pkg/appconsts/v1" appv2 "github.com/celestiaorg/celestia-app/v2/pkg/appconsts/v2" "github.com/celestiaorg/celestia-app/v2/pkg/proof" - "github.com/celestiaorg/celestia-app/v2/x/blob" blobkeeper "github.com/celestiaorg/celestia-app/v2/x/blob/keeper" blobtypes "github.com/celestiaorg/celestia-app/v2/x/blob/types" "github.com/celestiaorg/celestia-app/v2/x/paramfilter" "github.com/celestiaorg/celestia-app/v2/x/tokenfilter" - "github.com/celestiaorg/celestia-app/v2/x/blobstream" blobstreamkeeper "github.com/celestiaorg/celestia-app/v2/x/blobstream/keeper" blobstreamtypes "github.com/celestiaorg/celestia-app/v2/x/blobstream/types" "github.com/celestiaorg/celestia-app/v2/x/signal" @@ -105,59 +88,23 @@ import ( packetforwardkeeper "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v6/packetforward/keeper" packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v6/packetforward/types" - ica "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts" icahost "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/host" icahostkeeper "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/host/keeper" icahosttypes "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/host/types" icatypes "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/types" ) -var ( - // ModuleBasics defines the module BasicManager is in charge of setting up basic, - // non-dependant module elements, such as codec registration - // and genesis verification. - ModuleBasics = sdkmodule.NewBasicManager( - auth.AppModuleBasic{}, - genutil.AppModuleBasic{}, - bankModule{}, - capability.AppModuleBasic{}, - stakingModule{}, - mintModule{}, - distributionModule{}, - newGovModule(), - params.AppModuleBasic{}, - crisisModule{}, - slashingModule{}, - authzmodule.AppModuleBasic{}, - feegrantmodule.AppModuleBasic{}, - ibcModule{}, - evidence.AppModuleBasic{}, - transfer.AppModuleBasic{}, - vesting.AppModuleBasic{}, - blob.AppModuleBasic{}, - blobstream.AppModuleBasic{}, - signal.AppModuleBasic{}, - minfee.AppModuleBasic{}, - packetforward.AppModuleBasic{}, - icaModule{}, - ) - - // ModuleEncodingRegisters keeps track of all the module methods needed to - // register interfaces and specific type to encoding config - ModuleEncodingRegisters = extractRegisters(ModuleBasics) - - // maccPerms is short for module account permissions. - maccPerms = map[string][]string{ - authtypes.FeeCollectorName: nil, - distrtypes.ModuleName: nil, - govtypes.ModuleName: {authtypes.Burner}, - minttypes.ModuleName: {authtypes.Minter}, - stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking}, - stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, - ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, - icatypes.ModuleName: nil, - } -) +// maccPerms is short for module account permissions. +var maccPerms = map[string][]string{ + authtypes.FeeCollectorName: nil, + distrtypes.ModuleName: nil, + govtypes.ModuleName: {authtypes.Burner}, + minttypes.ModuleName: {authtypes.Minter}, + stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking}, + stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, + ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + icatypes.ModuleName: nil, +} const ( v1 = appv1.Version @@ -181,29 +128,31 @@ type App struct { invCheckPeriod uint // keys to access the substores - keys map[string]*storetypes.KVStoreKey - tkeys map[string]*storetypes.TransientStoreKey - memKeys map[string]*storetypes.MemoryStoreKey + keyVersions map[uint64][]string + keys map[string]*storetypes.KVStoreKey + tkeys map[string]*storetypes.TransientStoreKey + memKeys map[string]*storetypes.MemoryStoreKey // keepers - AccountKeeper authkeeper.AccountKeeper - BankKeeper bankkeeper.Keeper - AuthzKeeper authzkeeper.Keeper - CapabilityKeeper *capabilitykeeper.Keeper - StakingKeeper stakingkeeper.Keeper - SlashingKeeper slashingkeeper.Keeper - MintKeeper mintkeeper.Keeper - DistrKeeper distrkeeper.Keeper - GovKeeper govkeeper.Keeper - CrisisKeeper crisiskeeper.Keeper - UpgradeKeeper upgradekeeper.Keeper // This is included purely for the IBC Keeper. It is not used for upgrading - SignalKeeper signal.Keeper - ParamsKeeper paramskeeper.Keeper - IBCKeeper *ibckeeper.Keeper // IBCKeeper must be a pointer in the app, so we can SetRouter on it correctly - EvidenceKeeper evidencekeeper.Keeper - TransferKeeper ibctransferkeeper.Keeper - FeeGrantKeeper feegrantkeeper.Keeper - ICAHostKeeper icahostkeeper.Keeper + AccountKeeper authkeeper.AccountKeeper + BankKeeper bankkeeper.Keeper + AuthzKeeper authzkeeper.Keeper + CapabilityKeeper *capabilitykeeper.Keeper + StakingKeeper stakingkeeper.Keeper + SlashingKeeper slashingkeeper.Keeper + MintKeeper mintkeeper.Keeper + DistrKeeper distrkeeper.Keeper + GovKeeper govkeeper.Keeper + CrisisKeeper crisiskeeper.Keeper + UpgradeKeeper upgradekeeper.Keeper // This is included purely for the IBC Keeper. It is not used for upgrading + SignalKeeper signal.Keeper + ParamsKeeper paramskeeper.Keeper + IBCKeeper *ibckeeper.Keeper // IBCKeeper must be a pointer in the app, so we can SetRouter on it correctly + EvidenceKeeper evidencekeeper.Keeper + TransferKeeper ibctransferkeeper.Keeper + FeeGrantKeeper feegrantkeeper.Keeper + ICAHostKeeper icahostkeeper.Keeper + PacketForwardKeeper *packetforwardkeeper.Keeper ScopedIBCKeeper capabilitykeeper.ScopedKeeper // This keeper is public for test purposes ScopedTransferKeeper capabilitykeeper.ScopedKeeper // This keeper is public for test purposes @@ -218,8 +167,6 @@ type App struct { upgradeHeight int64 // used to define what messages are accepted for a given app version MsgGateKeeper *ante.MsgVersioningGateKeeper - - PacketForwardKeeper *packetforwardkeeper.Keeper } // New returns a reference to an initialized celestia app. @@ -231,7 +178,6 @@ func New( logger log.Logger, db dbm.DB, traceStore io.Writer, - loadLatest bool, invCheckPeriod uint, encodingConfig encoding.Config, upgradeHeight int64, @@ -247,18 +193,7 @@ func New( bApp.SetVersion(version.Version) bApp.SetInterfaceRegistry(interfaceRegistry) - keys := sdk.NewKVStoreKeys( - authtypes.StoreKey, authzkeeper.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, - minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, - govtypes.StoreKey, paramstypes.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey, - evidencetypes.StoreKey, capabilitytypes.StoreKey, - blobstreamtypes.StoreKey, - ibctransfertypes.StoreKey, - ibchost.StoreKey, - packetforwardtypes.StoreKey, - icahosttypes.StoreKey, - signaltypes.StoreKey, - ) + keys := sdk.NewKVStoreKeys(allStoreKeys()...) tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) @@ -268,6 +203,7 @@ func New( interfaceRegistry: interfaceRegistry, txConfig: encodingConfig.TxConfig, invCheckPeriod: invCheckPeriod, + keyVersions: versionedStoreKeys(), keys: keys, tkeys: tkeys, memKeys: memKeys, @@ -436,195 +372,14 @@ func New( // we prefer to be more strict in what arguments the modules expect. skipGenesisInvariants := cast.ToBool(appOpts.Get(crisis.FlagSkipGenesisInvariants)) - // NOTE: Any module instantiated in the module manager that is later modified - // must be passed by reference here. - var err error - app.mm, err = module.NewManager([]module.VersionedModule{ - { - Module: genutil.NewAppModule(app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx, encodingConfig.TxConfig), - FromVersion: v1, ToVersion: v2, - }, - { - Module: auth.NewAppModule(appCodec, app.AccountKeeper, nil), - FromVersion: v1, ToVersion: v2, - }, - { - Module: vesting.NewAppModule(app.AccountKeeper, app.BankKeeper), - FromVersion: v1, ToVersion: v2, - }, - { - Module: bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper), - FromVersion: v1, ToVersion: v2, - }, - { - Module: capability.NewAppModule(appCodec, *app.CapabilityKeeper), - FromVersion: v1, ToVersion: v2, - }, - { - Module: feegrantmodule.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), - FromVersion: v1, ToVersion: v2, - }, - { - Module: crisis.NewAppModule(&app.CrisisKeeper, skipGenesisInvariants), - FromVersion: v1, ToVersion: v2, - }, - { - Module: gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper), - FromVersion: v1, ToVersion: v2, - }, - { - Module: mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper), - FromVersion: v1, ToVersion: v2, - }, - { - Module: slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), - FromVersion: v1, ToVersion: v2, - }, - { - Module: distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), - FromVersion: v1, ToVersion: v2, - }, - { - Module: staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), - FromVersion: v1, ToVersion: v2, - }, - { - Module: evidence.NewAppModule(app.EvidenceKeeper), - FromVersion: v1, ToVersion: v2, - }, - { - Module: authzmodule.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), - FromVersion: v1, ToVersion: v2, - }, - { - Module: ibc.NewAppModule(app.IBCKeeper), - FromVersion: v1, ToVersion: v2, - }, - { - Module: params.NewAppModule(app.ParamsKeeper), - FromVersion: v1, ToVersion: v2, - }, - { - Module: transfer.NewAppModule(app.TransferKeeper), - FromVersion: v1, ToVersion: v2, - }, - { - Module: blob.NewAppModule(appCodec, app.BlobKeeper), - FromVersion: v1, ToVersion: v2, - }, - { - Module: blobstream.NewAppModule(appCodec, app.BlobstreamKeeper), - FromVersion: v1, ToVersion: v2, - }, - { - Module: signal.NewAppModule(app.SignalKeeper), - FromVersion: v2, ToVersion: v2, - }, - { - Module: minfee.NewAppModule(app.ParamsKeeper), - FromVersion: v2, ToVersion: v2, - }, - { - Module: packetforward.NewAppModule(app.PacketForwardKeeper), - FromVersion: v2, ToVersion: v2, - }, - { - Module: ica.NewAppModule(nil, &app.ICAHostKeeper), - FromVersion: v2, ToVersion: v2, - }, - }) + // NOTE: Modules can't be modified or else must be passed by reference to the module manager + err := app.setupModuleManager(skipGenesisInvariants) if err != nil { panic(err) } - // During begin block slashing happens after distr.BeginBlocker so that - // there is nothing left over in the validator fee pool, so as to keep the - // CanWithdrawInvariant invariant. - // NOTE: staking module is required if HistoricalEntries param > 0 - app.mm.SetOrderBeginBlockers( - capabilitytypes.ModuleName, - minttypes.ModuleName, - distrtypes.ModuleName, - slashingtypes.ModuleName, - evidencetypes.ModuleName, - stakingtypes.ModuleName, - ibchost.ModuleName, - ibctransfertypes.ModuleName, - feegrant.ModuleName, - authtypes.ModuleName, - banktypes.ModuleName, - crisistypes.ModuleName, - govtypes.ModuleName, - genutiltypes.ModuleName, - blobtypes.ModuleName, - blobstreamtypes.ModuleName, - paramstypes.ModuleName, - authz.ModuleName, - vestingtypes.ModuleName, - signaltypes.ModuleName, - minfee.ModuleName, - icatypes.ModuleName, - packetforwardtypes.ModuleName, - ) - - app.mm.SetOrderEndBlockers( - crisistypes.ModuleName, - govtypes.ModuleName, - stakingtypes.ModuleName, - capabilitytypes.ModuleName, - minttypes.ModuleName, - distrtypes.ModuleName, - slashingtypes.ModuleName, - evidencetypes.ModuleName, - ibchost.ModuleName, - ibctransfertypes.ModuleName, - feegrant.ModuleName, - authtypes.ModuleName, - banktypes.ModuleName, - genutiltypes.ModuleName, - blobtypes.ModuleName, - blobstreamtypes.ModuleName, - paramstypes.ModuleName, - authz.ModuleName, - vestingtypes.ModuleName, - signaltypes.ModuleName, - minfee.ModuleName, - packetforwardtypes.ModuleName, - icatypes.ModuleName, - ) - - // NOTE: The genutils module must occur after staking so that pools are - // properly initialized with tokens from genesis accounts. - // NOTE: Capability module must occur first so that it can initialize any capabilities - // so that other modules that want to create or claim capabilities afterwards in InitChain - // can do so safely. - // NOTE: The minfee module must occur before genutil so DeliverTx can - // successfully pass the fee checking logic - app.mm.SetOrderInitGenesis( - capabilitytypes.ModuleName, - authtypes.ModuleName, - banktypes.ModuleName, - distrtypes.ModuleName, - stakingtypes.ModuleName, - slashingtypes.ModuleName, - govtypes.ModuleName, - minttypes.ModuleName, - crisistypes.ModuleName, - ibchost.ModuleName, - minfee.ModuleName, - genutiltypes.ModuleName, - evidencetypes.ModuleName, - ibctransfertypes.ModuleName, - blobtypes.ModuleName, - blobstreamtypes.ModuleName, - vestingtypes.ModuleName, - feegrant.ModuleName, - paramstypes.ModuleName, - authz.ModuleName, - signaltypes.ModuleName, - packetforwardtypes.ModuleName, - icatypes.ModuleName, - ) + // order begin block, end block and init genesis + app.setModuleOrder() app.QueryRouter().AddRoute(proof.TxInclusionQueryPath, proof.QueryTxInclusionProof) app.QueryRouter().AddRoute(proof.ShareInclusionQueryPath, proof.QueryShareInclusionProof) @@ -639,8 +394,8 @@ func New( app.MsgGateKeeper = ante.NewMsgVersioningGateKeeper(app.configurator.GetAcceptedMessages()) app.MsgServiceRouter().SetCircuit(app.MsgGateKeeper) - // initialize stores - app.MountKVStores(keys) + // Initialize the KV stores for the base modules (e.g. params). The base modules will be included in every app version. + app.MountKVStores(app.baseKeys()) app.MountTransientStores(tkeys) app.MountMemoryStores(memKeys) @@ -661,10 +416,16 @@ func New( )) app.SetPostHandler(posthandler.New()) - if loadLatest { - if err := app.LoadLatestVersion(); err != nil { - tmos.Exit(err.Error()) - } + app.SetMigrateStoreFn(app.migrateCommitStore) + app.SetMigrateModuleFn(app.migrateModules) + + // assert that keys are present for all supported versions + app.assertAllKeysArePresent() + + // we don't seal the store until the app version has been initailised + // this will just initialize the base keys (i.e. the param store) + if err := app.CommitMultiStore().LoadLatestVersion(); err != nil { + tmos.Exit(err.Error()) } return app @@ -686,42 +447,66 @@ func (app *App) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.Respo if currentVersion == v1 { // check that we are at the height before the upgrade if req.Height == app.upgradeHeight-1 { - if err := app.Upgrade(ctx, currentVersion, currentVersion+1); err != nil { - panic(err) - } + app.SetInitialAppVersionInConsensusParams(ctx, v2) + app.SetAppVersion(ctx, v2) } // from v2 to v3 and onwards we use a signalling mechanism } else if shouldUpgrade, newVersion := app.SignalKeeper.ShouldUpgrade(); shouldUpgrade { // Version changes must be increasing. Downgrades are not permitted if newVersion > currentVersion { - if err := app.Upgrade(ctx, currentVersion, newVersion); err != nil { - panic(err) - } + app.SetAppVersion(ctx, newVersion) + app.SignalKeeper.ResetTally(ctx) } } return res } -func (app *App) Upgrade(ctx sdk.Context, fromVersion, toVersion uint64) error { - if err := app.mm.RunMigrations(ctx, app.configurator, fromVersion, toVersion); err != nil { - return err +// migrateCommitStore tells the baseapp during a version upgrade, which stores to add and which +// stores to remove +func (app *App) migrateCommitStore(fromVersion, toVersion uint64) (baseapp.StoreMigrations, error) { + oldStoreKeys := app.keyVersions[fromVersion] + newStoreKeys := app.keyVersions[toVersion] + result := baseapp.StoreMigrations{ + Added: make(map[string]*storetypes.KVStoreKey), + Deleted: make(map[string]*storetypes.KVStoreKey), + } + for _, oldKey := range oldStoreKeys { + if !slices.Contains(newStoreKeys, oldKey) { + result.Deleted[oldKey] = app.keys[oldKey] + } } - if toVersion == v2 { - // we need to set the app version in the param store for the first time - app.SetInitialAppVersionInConsensusParams(ctx, toVersion) + for _, newKey := range newStoreKeys { + if !slices.Contains(oldStoreKeys, newKey) { + result.Added[newKey] = app.keys[newKey] + } } - app.SetAppVersion(ctx, toVersion) - app.SignalKeeper.ResetTally(ctx) - return nil + return result, nil } -// InitChainer application update at chain initialization -func (app *App) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { - var genesisState GenesisState - if err := tmjson.Unmarshal(req.AppStateBytes, &genesisState); err != nil { - panic(err) +// migrateModules performs migrations on existing modules that have registered migrations +// between versions and initializes the state of new modules for the specified app version. +func (app *App) migrateModules(ctx sdk.Context, fromVersion, toVersion uint64) error { + return app.mm.RunMigrations(ctx, app.configurator, fromVersion, toVersion) +} + +// We wrap Info around baseapp so we can take the app version and +// setup the multicommit store. +func (app *App) Info(req abci.RequestInfo) abci.ResponseInfo { + resp := app.BaseApp.Info(req) + // mount the stores for the provided app version + if resp.AppVersion > 0 && !app.IsSealed() { + app.MountKVStores(app.versionedKeys(resp.AppVersion)) + if err := app.LoadLatestVersion(); err != nil { + panic(fmt.Sprintf("loading latest version: %s", err.Error())) + } } - // genesis must always contain the consensus params. The validator set howerver is derived from the + return resp +} + +// We wrap InitChain around baseapp so we can take the app version and +// setup the multicommit store. +func (app *App) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) { + // genesis must always contain the consensus params. The validator set however is derived from the // initial genesis state. The genesis must always contain a non zero app version which is the initial // version that the chain starts on if req.ConsensusParams == nil || req.ConsensusParams.Version == nil { @@ -730,6 +515,26 @@ func (app *App) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.Res if req.ConsensusParams.Version.AppVersion == 0 { panic("app version 0 is not accepted. Please set an app version in the genesis") } + + // mount the stores for the provided app version if it has not already been mounted + if app.AppVersion() == 0 && !app.IsSealed() { + app.MountKVStores(app.versionedKeys(req.ConsensusParams.Version.AppVersion)) + if err := app.LoadLatestVersion(); err != nil { + panic(fmt.Sprintf("loading latest version: %s", err.Error())) + } + } + + return app.BaseApp.InitChain(req) +} + +// InitChainer application update at chain initialization +func (app *App) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { + var genesisState GenesisState + if err := tmjson.Unmarshal(req.AppStateBytes, &genesisState); err != nil { + panic(err) + } + + app.UpgradeKeeper.SetModuleVersionMap(ctx, app.mm.GetVersionMap(req.ConsensusParams.Version.AppVersion)) return app.mm.InitGenesis(ctx, app.appCodec, genesisState, req.ConsensusParams.Version.AppVersion) } @@ -744,6 +549,28 @@ func (app *App) SupportedVersions() []uint64 { return app.mm.SupportedVersions() } +// versionedKeys returns the keys for the kv stores for a given app version +func (app *App) versionedKeys(appVersion uint64) map[string]*storetypes.KVStoreKey { + output := make(map[string]*storetypes.KVStoreKey) + if keys, exists := app.keyVersions[appVersion]; exists { + for _, moduleName := range keys { + if key, exists := app.keys[moduleName]; exists { + output[moduleName] = key + } + } + } + return output +} + +// baseKeys returns the base keys that are mounted to every version +func (app *App) baseKeys() map[string]*storetypes.KVStoreKey { + return map[string]*storetypes.KVStoreKey{ + // we need to know the app version to know what stores to mount + // thus the paramstore must always be a store that is mounted + paramstypes.StoreKey: app.keys[paramstypes.StoreKey], + } +} + // ModuleAccountAddrs returns all the app's module account addresses. func (app *App) ModuleAccountAddrs() map[string]bool { modAccAddrs := make(map[string]bool) @@ -899,16 +726,13 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino // extractRegisters isolates the encoding module registers from the module // manager, and appends any solo registers. -func extractRegisters(m sdkmodule.BasicManager, soloRegisters ...encoding.ModuleRegister) []encoding.ModuleRegister { +func extractRegisters(m sdkmodule.BasicManager) []encoding.ModuleRegister { // TODO: might be able to use some standard generics in go 1.18 - s := make([]encoding.ModuleRegister, len(m)+len(soloRegisters)) + s := make([]encoding.ModuleRegister, len(m)) i := 0 for _, v := range m { s[i] = v i++ } - for i, v := range soloRegisters { - s[i+len(m)] = v - } return s } diff --git a/app/app_test.go b/app/app_test.go index 10fae52405..b83a0f9c68 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -14,13 +14,12 @@ func TestNew(t *testing.T) { logger := log.NewNopLogger() db := tmdb.NewMemDB() traceStore := &NoopWriter{} - loadLatest := true invCheckPeriod := uint(1) encodingConfig := encoding.MakeConfig(app.ModuleEncodingRegisters...) upgradeHeight := int64(0) appOptions := NoopAppOptions{} - got := app.New(logger, db, traceStore, loadLatest, invCheckPeriod, encodingConfig, upgradeHeight, appOptions) + got := app.New(logger, db, traceStore, invCheckPeriod, encodingConfig, upgradeHeight, appOptions) t.Run("initializes ICAHostKeeper", func(t *testing.T) { assert.NotNil(t, got.ICAHostKeeper) diff --git a/app/module/module.go b/app/module/module.go index ce782e7728..ac986533c0 100644 --- a/app/module/module.go +++ b/app/module/module.go @@ -223,9 +223,6 @@ func (m *Manager) assertNoForgottenModules(setOrderFnName string, moduleNames [] // MigrationHandler is the migration function that each module registers. type MigrationHandler func(sdk.Context) error -// VersionMap is a map of moduleName -> version -type VersionMap map[string]uint64 - // RunMigrations performs in-place store migrations for all modules. This // function MUST be called when the state machine changes appVersion func (m Manager) RunMigrations(ctx sdk.Context, cfg sdkmodule.Configurator, fromVersion, toVersion uint64) error { @@ -335,6 +332,22 @@ func (m *Manager) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) abci.Respo } } +// GetVersionMap gets consensus version from all modules +func (m *Manager) GetVersionMap(version uint64) sdkmodule.VersionMap { + vermap := make(sdkmodule.VersionMap) + if version > m.lastVersion || version < m.firstVersion { + return vermap + } + + for _, v := range m.versionedModules[version] { + version := v.ConsensusVersion() + name := v.Name() + vermap[name] = version + } + + return vermap +} + // ModuleNames returns list of all module names, without any particular order. func (m *Manager) ModuleNames(version uint64) []string { modules, ok := m.versionedModules[version] @@ -351,6 +364,7 @@ func (m *Manager) ModuleNames(version uint64) []string { return ms } +// SupportedVersions returns all the supported versions for the module manager func (m *Manager) SupportedVersions() []uint64 { output := make([]uint64, 0, m.lastVersion-m.firstVersion+1) for version := m.firstVersion; version <= m.lastVersion; version++ { @@ -385,6 +399,17 @@ func (m *Manager) checkUpgradeSchedule() error { return nil } +// assertMatchingModules performs a sanity check that the basic module manager +// contains all the same modules present in the module manager +func (m *Manager) AssertMatchingModules(basicModuleManager sdkmodule.BasicManager) error { + for _, module := range m.allModules { + if _, exists := basicModuleManager[module.Name()]; !exists { + return fmt.Errorf("module %s not found in basic module manager", module.Name()) + } + } + return nil +} + // DefaultMigrationsOrder returns a default migrations order: ascending alphabetical by module name, // except x/auth which will run last, see: // https://github.com/cosmos/cosmos-sdk/issues/10591 diff --git a/app/modules.go b/app/modules.go new file mode 100644 index 0000000000..210d0f75cd --- /dev/null +++ b/app/modules.go @@ -0,0 +1,373 @@ +package app + +import ( + "fmt" + + "github.com/celestiaorg/celestia-app/v2/app/module" + "github.com/celestiaorg/celestia-app/v2/x/blob" + blobtypes "github.com/celestiaorg/celestia-app/v2/x/blob/types" + "github.com/celestiaorg/celestia-app/v2/x/blobstream" + blobstreamtypes "github.com/celestiaorg/celestia-app/v2/x/blobstream/types" + "github.com/celestiaorg/celestia-app/v2/x/minfee" + "github.com/celestiaorg/celestia-app/v2/x/mint" + minttypes "github.com/celestiaorg/celestia-app/v2/x/mint/types" + "github.com/celestiaorg/celestia-app/v2/x/signal" + signaltypes "github.com/celestiaorg/celestia-app/v2/x/signal/types" + sdkmodule "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/auth" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/auth/vesting" + vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + "github.com/cosmos/cosmos-sdk/x/authz" + authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" + authzmodule "github.com/cosmos/cosmos-sdk/x/authz/module" + "github.com/cosmos/cosmos-sdk/x/bank" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/capability" + capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types" + "github.com/cosmos/cosmos-sdk/x/crisis" + crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" + distr "github.com/cosmos/cosmos-sdk/x/distribution" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" + "github.com/cosmos/cosmos-sdk/x/evidence" + evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" + "github.com/cosmos/cosmos-sdk/x/feegrant" + feegrantmodule "github.com/cosmos/cosmos-sdk/x/feegrant/module" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/cosmos/cosmos-sdk/x/gov" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/cosmos-sdk/x/params" + paramstypes "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/cosmos/cosmos-sdk/x/slashing" + slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" + "github.com/cosmos/cosmos-sdk/x/staking" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v6/packetforward" + packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v6/packetforward/types" + ica "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts" + icahosttypes "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/host/types" + icatypes "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/types" + "github.com/cosmos/ibc-go/v6/modules/apps/transfer" + ibctransfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" + ibc "github.com/cosmos/ibc-go/v6/modules/core" + ibchost "github.com/cosmos/ibc-go/v6/modules/core/24-host" +) + +var ( + // ModuleBasics defines the module BasicManager is in charge of setting up basic, + // non-dependant module elements, such as codec registration + // and genesis verification. + ModuleBasics = sdkmodule.NewBasicManager( + auth.AppModuleBasic{}, + genutil.AppModuleBasic{}, + bankModule{}, + capability.AppModuleBasic{}, + stakingModule{}, + mintModule{}, + distributionModule{}, + newGovModule(), + params.AppModuleBasic{}, + crisisModule{}, + slashingModule{}, + authzmodule.AppModuleBasic{}, + feegrantmodule.AppModuleBasic{}, + ibcModule{}, + evidence.AppModuleBasic{}, + transfer.AppModuleBasic{}, + vesting.AppModuleBasic{}, + blob.AppModuleBasic{}, + blobstream.AppModuleBasic{}, + signal.AppModuleBasic{}, + minfee.AppModuleBasic{}, + packetforward.AppModuleBasic{}, + icaModule{}, + ) + + // ModuleEncodingRegisters keeps track of all the module methods needed to + // register interfaces and specific type to encoding config + ModuleEncodingRegisters = extractRegisters(ModuleBasics) +) + +func (app *App) setupModuleManager(skipGenesisInvariants bool) error { + var err error + app.mm, err = module.NewManager([]module.VersionedModule{ + { + Module: genutil.NewAppModule(app.AccountKeeper, app.StakingKeeper, app.BaseApp.DeliverTx, app.txConfig), + FromVersion: v1, ToVersion: v2, + }, + { + Module: auth.NewAppModule(app.appCodec, app.AccountKeeper, nil), + FromVersion: v1, ToVersion: v2, + }, + { + Module: vesting.NewAppModule(app.AccountKeeper, app.BankKeeper), + FromVersion: v1, ToVersion: v2, + }, + { + Module: bank.NewAppModule(app.appCodec, app.BankKeeper, app.AccountKeeper), + FromVersion: v1, ToVersion: v2, + }, + { + Module: capability.NewAppModule(app.appCodec, *app.CapabilityKeeper), + FromVersion: v1, ToVersion: v2, + }, + { + Module: feegrantmodule.NewAppModule(app.appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry), + FromVersion: v1, ToVersion: v2, + }, + { + Module: crisis.NewAppModule(&app.CrisisKeeper, skipGenesisInvariants), + FromVersion: v1, ToVersion: v2, + }, + { + Module: gov.NewAppModule(app.appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper), + FromVersion: v1, ToVersion: v2, + }, + { + Module: mint.NewAppModule(app.appCodec, app.MintKeeper, app.AccountKeeper), + FromVersion: v1, ToVersion: v2, + }, + { + Module: slashing.NewAppModule(app.appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), + FromVersion: v1, ToVersion: v2, + }, + { + Module: distr.NewAppModule(app.appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), + FromVersion: v1, ToVersion: v2, + }, + { + Module: staking.NewAppModule(app.appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), + FromVersion: v1, ToVersion: v2, + }, + { + Module: evidence.NewAppModule(app.EvidenceKeeper), + FromVersion: v1, ToVersion: v2, + }, + { + Module: authzmodule.NewAppModule(app.appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry), + FromVersion: v1, ToVersion: v2, + }, + { + Module: ibc.NewAppModule(app.IBCKeeper), + FromVersion: v1, ToVersion: v2, + }, + { + Module: params.NewAppModule(app.ParamsKeeper), + FromVersion: v1, ToVersion: v2, + }, + { + Module: transfer.NewAppModule(app.TransferKeeper), + FromVersion: v1, ToVersion: v2, + }, + { + Module: blob.NewAppModule(app.appCodec, app.BlobKeeper), + FromVersion: v1, ToVersion: v2, + }, + { + Module: blobstream.NewAppModule(app.appCodec, app.BlobstreamKeeper), + FromVersion: v1, ToVersion: v2, + }, + { + Module: signal.NewAppModule(app.SignalKeeper), + FromVersion: v2, ToVersion: v2, + }, + { + Module: minfee.NewAppModule(app.ParamsKeeper), + FromVersion: v2, ToVersion: v2, + }, + { + Module: packetforward.NewAppModule(app.PacketForwardKeeper), + FromVersion: v2, ToVersion: v2, + }, + { + Module: ica.NewAppModule(nil, &app.ICAHostKeeper), + FromVersion: v2, ToVersion: v2, + }, + }) + if err != nil { + return err + } + return app.mm.AssertMatchingModules(ModuleBasics) +} + +func (app *App) setModuleOrder() { + // During begin block slashing happens after distr.BeginBlocker so that + // there is nothing left over in the validator fee pool, so as to keep the + // CanWithdrawInvariant invariant. + // NOTE: staking module is required if HistoricalEntries param > 0 + app.mm.SetOrderBeginBlockers( + capabilitytypes.ModuleName, + minttypes.ModuleName, + distrtypes.ModuleName, + slashingtypes.ModuleName, + evidencetypes.ModuleName, + stakingtypes.ModuleName, + ibchost.ModuleName, + ibctransfertypes.ModuleName, + feegrant.ModuleName, + authtypes.ModuleName, + banktypes.ModuleName, + crisistypes.ModuleName, + govtypes.ModuleName, + genutiltypes.ModuleName, + blobtypes.ModuleName, + blobstreamtypes.ModuleName, + paramstypes.ModuleName, + authz.ModuleName, + vestingtypes.ModuleName, + signaltypes.ModuleName, + minfee.ModuleName, + icatypes.ModuleName, + packetforwardtypes.ModuleName, + ) + + app.mm.SetOrderEndBlockers( + crisistypes.ModuleName, + govtypes.ModuleName, + stakingtypes.ModuleName, + capabilitytypes.ModuleName, + minttypes.ModuleName, + distrtypes.ModuleName, + slashingtypes.ModuleName, + evidencetypes.ModuleName, + ibchost.ModuleName, + ibctransfertypes.ModuleName, + feegrant.ModuleName, + authtypes.ModuleName, + banktypes.ModuleName, + genutiltypes.ModuleName, + blobtypes.ModuleName, + blobstreamtypes.ModuleName, + paramstypes.ModuleName, + authz.ModuleName, + vestingtypes.ModuleName, + signaltypes.ModuleName, + minfee.ModuleName, + packetforwardtypes.ModuleName, + icatypes.ModuleName, + ) + + // NOTE: The genutils module must occur after staking so that pools are + // properly initialized with tokens from genesis accounts. + // NOTE: Capability module must occur first so that it can initialize any capabilities + // so that other modules that want to create or claim capabilities afterwards in InitChain + // can do so safely. + // NOTE: The minfee module must occur before genutil so DeliverTx can + // successfully pass the fee checking logic + app.mm.SetOrderInitGenesis( + capabilitytypes.ModuleName, + authtypes.ModuleName, + banktypes.ModuleName, + distrtypes.ModuleName, + stakingtypes.ModuleName, + slashingtypes.ModuleName, + govtypes.ModuleName, + minttypes.ModuleName, + crisistypes.ModuleName, + ibchost.ModuleName, + minfee.ModuleName, + genutiltypes.ModuleName, + evidencetypes.ModuleName, + ibctransfertypes.ModuleName, + blobtypes.ModuleName, + blobstreamtypes.ModuleName, + vestingtypes.ModuleName, + feegrant.ModuleName, + paramstypes.ModuleName, + authz.ModuleName, + signaltypes.ModuleName, + packetforwardtypes.ModuleName, + icatypes.ModuleName, + ) +} + +func allStoreKeys() []string { + return []string{ + authtypes.StoreKey, authzkeeper.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, + minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey, + govtypes.StoreKey, paramstypes.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey, + evidencetypes.StoreKey, capabilitytypes.StoreKey, + blobstreamtypes.StoreKey, + ibctransfertypes.StoreKey, + ibchost.StoreKey, + packetforwardtypes.StoreKey, + icahosttypes.StoreKey, + signaltypes.StoreKey, + blobtypes.StoreKey, + } +} + +// versionedStoreKeys returns the store keys for each app version +// ... I wish there was an easier way than this (like using the modules which are already versioned) +func versionedStoreKeys() map[uint64][]string { + return map[uint64][]string{ + 1: { + authtypes.StoreKey, + authzkeeper.StoreKey, + banktypes.StoreKey, + stakingtypes.StoreKey, + minttypes.StoreKey, + distrtypes.StoreKey, + slashingtypes.StoreKey, + govtypes.StoreKey, + upgradetypes.StoreKey, + feegrant.StoreKey, + evidencetypes.StoreKey, + capabilitytypes.StoreKey, + blobstreamtypes.StoreKey, + ibctransfertypes.StoreKey, + ibchost.StoreKey, + blobtypes.StoreKey, + }, + 2: { + authtypes.StoreKey, + authzkeeper.StoreKey, + banktypes.StoreKey, + stakingtypes.StoreKey, + minttypes.StoreKey, + distrtypes.StoreKey, + slashingtypes.StoreKey, + govtypes.StoreKey, + upgradetypes.StoreKey, + feegrant.StoreKey, + evidencetypes.StoreKey, + capabilitytypes.StoreKey, + blobstreamtypes.StoreKey, + ibctransfertypes.StoreKey, + ibchost.StoreKey, + packetforwardtypes.StoreKey, + icahosttypes.StoreKey, + signaltypes.StoreKey, + }, + } +} + +// assertAllKeysArePresent performs a couple sanity checks on startup to ensure each versions key names have +// a key and that all versions supported by the module manager have a respective versioned key +func (app *App) assertAllKeysArePresent() { + supportedAppVersions := app.SupportedVersions() + supportedVersionsMap := make(map[uint64]bool, len(supportedAppVersions)) + for _, version := range supportedAppVersions { + supportedVersionsMap[version] = false + } + + for appVersion, keys := range app.keyVersions { + if _, exists := supportedVersionsMap[appVersion]; exists { + supportedVersionsMap[appVersion] = true + } else { + panic(fmt.Sprintf("keys %v for app version %d are not supported by the module manager", keys, appVersion)) + } + for _, key := range keys { + if _, ok := app.keys[key]; !ok { + panic(fmt.Sprintf("key %s is not present", key)) + } + } + } + for appVersion, supported := range supportedVersionsMap { + if !supported { + panic(fmt.Sprintf("app version %d is supported by the module manager but has no keys", appVersion)) + } + } +} diff --git a/app/test/ica_upgrade_test.go b/app/test/ica_upgrade_test.go index 5a1fc8e022..6a86ba42b1 100644 --- a/app/test/ica_upgrade_test.go +++ b/app/test/ica_upgrade_test.go @@ -1,30 +1,21 @@ package app_test import ( - "encoding/json" "testing" - "time" - "github.com/celestiaorg/celestia-app/v2/app" - "github.com/celestiaorg/celestia-app/v2/app/encoding" - v1 "github.com/celestiaorg/celestia-app/v2/pkg/appconsts/v1" - v2 "github.com/celestiaorg/celestia-app/v2/pkg/appconsts/v2" "github.com/celestiaorg/celestia-app/v2/test/util" - "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/x/params/types/proposal" icahosttypes "github.com/cosmos/ibc-go/v6/modules/apps/27-interchain-accounts/host/types" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" version "github.com/tendermint/tendermint/proto/tendermint/version" - dbm "github.com/tendermint/tm-db" ) // TestICA verifies that the ICA module's params are overridden during an // upgrade from v1 -> v2. func TestICA(t *testing.T) { - testApp, _ := setupTestApp(t, 3) + testApp, _ := util.SetupTestAppWithUpgradeHeight(t, 3) ctx := testApp.NewContext(true, tmproto.Header{ Version: version.Consensus{ App: 1, @@ -57,46 +48,3 @@ func TestICA(t *testing.T) { require.NoError(t, err) require.Equal(t, "true", got.Param.Value) } - -func setupTestApp(t *testing.T, upgradeHeight int64) (*app.App, keyring.Keyring) { - t.Helper() - - db := dbm.NewMemDB() - chainID := "test_chain" - encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) - testApp := app.New(log.NewNopLogger(), db, nil, true, 0, encCfg, upgradeHeight, util.EmptyAppOptions{}) - genesisState, _, kr := util.GenesisStateWithSingleValidator(testApp, "account") - stateBytes, err := json.MarshalIndent(genesisState, "", " ") - require.NoError(t, err) - infoResp := testApp.Info(abci.RequestInfo{}) - require.EqualValues(t, 0, infoResp.AppVersion) - cp := app.DefaultInitialConsensusParams() - abciParams := &abci.ConsensusParams{ - Block: &abci.BlockParams{ - MaxBytes: cp.Block.MaxBytes, - MaxGas: cp.Block.MaxGas, - }, - Evidence: &cp.Evidence, - Validator: &cp.Validator, - Version: &cp.Version, - } - - _ = testApp.InitChain( - abci.RequestInitChain{ - Time: time.Now(), - Validators: []abci.ValidatorUpdate{}, - ConsensusParams: abciParams, - AppStateBytes: stateBytes, - ChainId: chainID, - }, - ) - - // assert that the chain starts with version provided in genesis - infoResp = testApp.Info(abci.RequestInfo{}) - require.EqualValues(t, app.DefaultInitialConsensusParams().Version.AppVersion, infoResp.AppVersion) - - _ = testApp.Commit() - supportedVersions := []uint64{v1.Version, v2.Version} - require.Equal(t, supportedVersions, testApp.SupportedVersions()) - return testApp, kr -} diff --git a/app/test/upgrade_test.go b/app/test/upgrade_test.go index 4cea521252..441586c9ad 100644 --- a/app/test/upgrade_test.go +++ b/app/test/upgrade_test.go @@ -5,6 +5,7 @@ import ( v1 "github.com/celestiaorg/celestia-app/v2/pkg/appconsts/v1" v2 "github.com/celestiaorg/celestia-app/v2/pkg/appconsts/v2" + "github.com/celestiaorg/celestia-app/v2/test/util" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" @@ -12,7 +13,7 @@ import ( ) func TestUpgradeAppVersion(t *testing.T) { - testApp, _ := setupTestApp(t, 3) + testApp, _ := util.SetupTestAppWithUpgradeHeight(t, 3) supportedVersions := []uint64{v1.Version, v2.Version} diff --git a/cmd/celestia-appd/cmd/root.go b/cmd/celestia-appd/cmd/root.go index 3dbcc66fba..61dc7e3265 100644 --- a/cmd/celestia-appd/cmd/root.go +++ b/cmd/celestia-appd/cmd/root.go @@ -236,7 +236,7 @@ func NewAppServer(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts se } return app.New( - logger, db, traceStore, true, + logger, db, traceStore, cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)), encoding.MakeConfig(app.ModuleEncodingRegisters...), // Ideally, we would reuse the one created by NewRootCmd. cast.ToInt64(appOpts.Get(UpgradeHeightFlag)), @@ -262,13 +262,13 @@ func createAppAndExport( encCfg.Codec = codec.NewProtoCodec(encCfg.InterfaceRegistry) var capp *app.App if height != -1 { - capp = app.New(logger, db, traceStore, false, uint(1), encCfg, 0, appOpts) + capp = app.New(logger, db, traceStore, uint(1), encCfg, 0, appOpts) if err := capp.LoadHeight(height); err != nil { return servertypes.ExportedApp{}, err } } else { - capp = app.New(logger, db, traceStore, true, uint(1), encCfg, 0, appOpts) + capp = app.New(logger, db, traceStore, uint(1), encCfg, 0, appOpts) } return capp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) diff --git a/go.mod b/go.mod index c130d8f38d..6558e1772d 100644 --- a/go.mod +++ b/go.mod @@ -257,7 +257,7 @@ require ( ) replace ( - github.com/cosmos/cosmos-sdk => github.com/celestiaorg/cosmos-sdk v1.20.2-sdk-v0.46.16 + github.com/cosmos/cosmos-sdk => github.com/celestiaorg/cosmos-sdk v1.21.0-sdk-v0.46.16 // Pin to ledger-cosmos-go v0.12.4 to avoid a breaking change introduced in v0.13.0 // The following replace statement can be removed when we upgrade to cosmos-sdk >= v0.50.0 github.com/cosmos/ledger-cosmos-go => github.com/cosmos/ledger-cosmos-go v0.12.4 diff --git a/go.sum b/go.sum index 9312598454..829e457d4d 100644 --- a/go.sum +++ b/go.sum @@ -326,8 +326,8 @@ github.com/celestiaorg/blobstream-contracts/v3 v3.1.0 h1:h1Y4V3EMQ2mFmNtWt2sIhZI github.com/celestiaorg/blobstream-contracts/v3 v3.1.0/go.mod h1:x4DKyfKOSv1ZJM9NwV+Pw01kH2CD7N5zTFclXIVJ6GQ= github.com/celestiaorg/celestia-core v1.35.0-tm-v0.34.29 h1:sXERzNXgyHyqTKNQx4S29C/NMDzgav62DaQDNF49HUQ= github.com/celestiaorg/celestia-core v1.35.0-tm-v0.34.29/go.mod h1:weZR4wYx1Vcw3g1Jc5G8VipG4M+KUDSqeIzyyWszmsQ= -github.com/celestiaorg/cosmos-sdk v1.20.2-sdk-v0.46.16 h1:EmzwDx64oG8eKZPar50VCkpzxDNqjjtt7rOBJsMQY8E= -github.com/celestiaorg/cosmos-sdk v1.20.2-sdk-v0.46.16/go.mod h1:AmuR63HTlX8vNV3+NGWNPIZa95J1UweTUmWDHSdTGj0= +github.com/celestiaorg/cosmos-sdk v1.21.0-sdk-v0.46.16 h1:P44npoIUuortDf0nQSUpTwO8zsWoHufbDD/doU+CtxY= +github.com/celestiaorg/cosmos-sdk v1.21.0-sdk-v0.46.16/go.mod h1:AmuR63HTlX8vNV3+NGWNPIZa95J1UweTUmWDHSdTGj0= github.com/celestiaorg/go-square v1.0.1 h1:LEG1zrw4i03VBMElQF8GAbKYgh1bT1uGzWxasU2ePuo= github.com/celestiaorg/go-square v1.0.1/go.mod h1:XMv5SGCeGSkynW2OOsedugaW/rQlvzxGzWGxTKsyYOU= github.com/celestiaorg/go-square/merkle v0.0.0-20240117232118-fd78256df076 h1:PYInrsYzrDIsZW9Yb86OTi2aEKuPcpgJt6Mc0Jlc/yg= diff --git a/test/packetforward/packetforward_upgrade_test.go b/test/packetforward/packetforward_upgrade_test.go index d35f1057df..45e5de5994 100644 --- a/test/packetforward/packetforward_upgrade_test.go +++ b/test/packetforward/packetforward_upgrade_test.go @@ -1,31 +1,24 @@ package packetforward_test import ( - "encoding/json" "strings" "testing" - "time" - "github.com/celestiaorg/celestia-app/v2/app" - "github.com/celestiaorg/celestia-app/v2/app/encoding" v1 "github.com/celestiaorg/celestia-app/v2/pkg/appconsts/v1" v2 "github.com/celestiaorg/celestia-app/v2/pkg/appconsts/v2" "github.com/celestiaorg/celestia-app/v2/test/util" - "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/x/params/types/proposal" packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v6/packetforward/types" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" version "github.com/tendermint/tendermint/proto/tendermint/version" - dbm "github.com/tendermint/tm-db" ) // TestPacketForwardMiddlewareAgainstAppUpgrades verifies that the PFM module's params are overridden during an // upgrade from v1 -> v2. func TestPacketForwardMiddlewareAgainstAppUpgrades(t *testing.T) { - testApp, _ := setupTestApp(t, 3) + testApp, _ := util.SetupTestAppWithUpgradeHeight(t, 3) supportedVersions := []uint64{v1.Version, v2.Version} require.Equal(t, supportedVersions, testApp.SupportedVersions()) @@ -74,44 +67,3 @@ func TestPacketForwardMiddlewareAgainstAppUpgrades(t *testing.T) { require.NoError(t, err) require.Equal(t, "0.000000000000000000", strings.Trim(got.Param.Value, "\"")) } - -func setupTestApp(t *testing.T, upgradeHeight int64) (*app.App, keyring.Keyring) { - t.Helper() - - db := dbm.NewMemDB() - chainID := "test_chain" - encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) - testApp := app.New(log.NewNopLogger(), db, nil, true, 0, encCfg, upgradeHeight, util.EmptyAppOptions{}) - genesisState, _, kr := util.GenesisStateWithSingleValidator(testApp, "account") - stateBytes, err := json.MarshalIndent(genesisState, "", " ") - require.NoError(t, err) - infoResp := testApp.Info(abci.RequestInfo{}) - require.EqualValues(t, 0, infoResp.AppVersion) - cp := app.DefaultInitialConsensusParams() - abciParams := &abci.ConsensusParams{ - Block: &abci.BlockParams{ - MaxBytes: cp.Block.MaxBytes, - MaxGas: cp.Block.MaxGas, - }, - Evidence: &cp.Evidence, - Validator: &cp.Validator, - Version: &cp.Version, - } - - _ = testApp.InitChain( - abci.RequestInitChain{ - Time: time.Now(), - Validators: []abci.ValidatorUpdate{}, - ConsensusParams: abciParams, - AppStateBytes: stateBytes, - ChainId: chainID, - }, - ) - - // assert that the chain starts with version provided in genesis - infoResp = testApp.Info(abci.RequestInfo{}) - require.EqualValues(t, app.DefaultInitialConsensusParams().Version.AppVersion, infoResp.AppVersion) - - _ = testApp.Commit() - return testApp, kr -} diff --git a/test/testground/go.mod b/test/testground/go.mod index 294efae041..9bb25f037e 100644 --- a/test/testground/go.mod +++ b/test/testground/go.mod @@ -222,7 +222,7 @@ require ( ) replace ( - github.com/cosmos/cosmos-sdk => github.com/celestiaorg/cosmos-sdk v1.20.2-sdk-v0.46.16 + github.com/cosmos/cosmos-sdk => github.com/celestiaorg/cosmos-sdk v1.21.0-sdk-v0.46.16 github.com/dgrijalva/jwt-go => github.com/golang-jwt/jwt/v4 v4.4.2 github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.9.0 github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 diff --git a/test/testground/go.sum b/test/testground/go.sum index fbe539fd54..77f3db8da5 100644 --- a/test/testground/go.sum +++ b/test/testground/go.sum @@ -340,8 +340,8 @@ github.com/celestiaorg/celestia-app v1.0.0-rc0.0.20240304150808-f0a1f87c0253 h1: github.com/celestiaorg/celestia-app v1.0.0-rc0.0.20240304150808-f0a1f87c0253/go.mod h1:z3gMQZkUUe2MYrQQGnrYy+gDP0QpX0f5EPWtVNM0u/E= github.com/celestiaorg/celestia-core v1.35.0-tm-v0.34.29 h1:sXERzNXgyHyqTKNQx4S29C/NMDzgav62DaQDNF49HUQ= github.com/celestiaorg/celestia-core v1.35.0-tm-v0.34.29/go.mod h1:weZR4wYx1Vcw3g1Jc5G8VipG4M+KUDSqeIzyyWszmsQ= -github.com/celestiaorg/cosmos-sdk v1.20.2-sdk-v0.46.16 h1:EmzwDx64oG8eKZPar50VCkpzxDNqjjtt7rOBJsMQY8E= -github.com/celestiaorg/cosmos-sdk v1.20.2-sdk-v0.46.16/go.mod h1:AmuR63HTlX8vNV3+NGWNPIZa95J1UweTUmWDHSdTGj0= +github.com/celestiaorg/cosmos-sdk v1.21.0-sdk-v0.46.16 h1:P44npoIUuortDf0nQSUpTwO8zsWoHufbDD/doU+CtxY= +github.com/celestiaorg/cosmos-sdk v1.21.0-sdk-v0.46.16/go.mod h1:AmuR63HTlX8vNV3+NGWNPIZa95J1UweTUmWDHSdTGj0= github.com/celestiaorg/go-square v1.0.1 h1:LEG1zrw4i03VBMElQF8GAbKYgh1bT1uGzWxasU2ePuo= github.com/celestiaorg/go-square v1.0.1/go.mod h1:XMv5SGCeGSkynW2OOsedugaW/rQlvzxGzWGxTKsyYOU= github.com/celestiaorg/go-square/merkle v0.0.0-20240117232118-fd78256df076 h1:PYInrsYzrDIsZW9Yb86OTi2aEKuPcpgJt6Mc0Jlc/yg= diff --git a/test/tokenfilter/setup.go b/test/tokenfilter/setup.go index a900004262..aea94a1fc6 100644 --- a/test/tokenfilter/setup.go +++ b/test/tokenfilter/setup.go @@ -146,7 +146,7 @@ func SetupWithGenesisValSet(t testing.TB, valSet *tmtypes.ValidatorSet, genAccs encCdc := encoding.MakeConfig(app.ModuleEncodingRegisters...) genesisState := app.NewDefaultGenesisState(encCdc.Codec) app := app.New( - log.NewNopLogger(), db, nil, true, 5, encCdc, 0, simapp.EmptyAppOptions{}, + log.NewNopLogger(), db, nil, 5, encCdc, 0, simapp.EmptyAppOptions{}, ) // set genesis accounts diff --git a/test/util/malicious/app.go b/test/util/malicious/app.go index fdd0263abc..9212e966d7 100644 --- a/test/util/malicious/app.go +++ b/test/util/malicious/app.go @@ -51,13 +51,12 @@ func New( logger log.Logger, db dbm.DB, traceStore io.Writer, - loadLatest bool, invCheckPeriod uint, encodingConfig encoding.Config, appOpts servertypes.AppOptions, baseAppOptions ...func(*baseapp.BaseApp), ) *App { - goodApp := app.New(logger, db, traceStore, loadLatest, invCheckPeriod, encodingConfig, 0, appOpts, baseAppOptions...) + goodApp := app.New(logger, db, traceStore, invCheckPeriod, encodingConfig, 0, appOpts, baseAppOptions...) badApp := &App{App: goodApp} // set the malicious prepare proposal handler if it is set in the app options diff --git a/test/util/malicious/test_app.go b/test/util/malicious/test_app.go index 24b3f652c5..35943f611f 100644 --- a/test/util/malicious/test_app.go +++ b/test/util/malicious/test_app.go @@ -77,7 +77,7 @@ func NewAppServer(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts se } return New( - logger, db, traceStore, true, + logger, db, traceStore, cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)), encoding.MakeConfig(app.ModuleEncodingRegisters...), // Ideally, we would reuse the one created by NewRootCmd. appOpts, diff --git a/test/util/test_app.go b/test/util/test_app.go index 59b7f3cb26..765b53d326 100644 --- a/test/util/test_app.go +++ b/test/util/test_app.go @@ -3,11 +3,14 @@ package util import ( "encoding/json" "fmt" + "testing" "time" "github.com/celestiaorg/celestia-app/v2/app" "github.com/celestiaorg/celestia-app/v2/app/encoding" "github.com/celestiaorg/celestia-app/v2/pkg/appconsts" + v1 "github.com/celestiaorg/celestia-app/v2/pkg/appconsts/v1" + v2 "github.com/celestiaorg/celestia-app/v2/pkg/appconsts/v2" "github.com/celestiaorg/celestia-app/v2/test/util/testfactory" "github.com/celestiaorg/celestia-app/v2/test/util/testnode" "github.com/cosmos/cosmos-sdk/codec" @@ -22,6 +25,7 @@ import ( authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/spf13/cast" + "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" @@ -79,7 +83,7 @@ func NewTestAppWithGenesisSet(cparams *tmproto.ConsensusParams, genAccounts ...s encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) testApp := app.New( - log.NewNopLogger(), db, nil, true, + log.NewNopLogger(), db, nil, cast.ToUint(emptyOpts.Get(server.FlagInvCheckPeriod)), encCfg, 0, @@ -107,6 +111,8 @@ func NewTestAppWithGenesisSet(cparams *tmproto.ConsensusParams, genAccounts ...s genesisTime := time.Date(2023, 1, 1, 1, 1, 1, 1, time.UTC).UTC() + _ = testApp.Info(abci.RequestInfo{}) + // init chain will set the validator set and initialize the genesis accounts testApp.InitChain( abci.RequestInitChain{ @@ -293,3 +299,46 @@ func genesisStateWithValSet( func NewDefaultGenesisState(cdc codec.JSONCodec) app.GenesisState { return app.ModuleBasics.DefaultGenesis(cdc) } + +func SetupTestAppWithUpgradeHeight(t *testing.T, upgradeHeight int64) (*app.App, keyring.Keyring) { + t.Helper() + + db := dbm.NewMemDB() + chainID := "test_chain" + encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) + testApp := app.New(log.NewNopLogger(), db, nil, 0, encCfg, upgradeHeight, EmptyAppOptions{}) + genesisState, _, kr := GenesisStateWithSingleValidator(testApp, "account") + stateBytes, err := json.MarshalIndent(genesisState, "", " ") + require.NoError(t, err) + infoResp := testApp.Info(abci.RequestInfo{}) + require.EqualValues(t, 0, infoResp.AppVersion) + cp := app.DefaultInitialConsensusParams() + abciParams := &abci.ConsensusParams{ + Block: &abci.BlockParams{ + MaxBytes: cp.Block.MaxBytes, + MaxGas: cp.Block.MaxGas, + }, + Evidence: &cp.Evidence, + Validator: &cp.Validator, + Version: &cp.Version, + } + + _ = testApp.InitChain( + abci.RequestInitChain{ + Time: time.Now(), + Validators: []abci.ValidatorUpdate{}, + ConsensusParams: abciParams, + AppStateBytes: stateBytes, + ChainId: chainID, + }, + ) + + // assert that the chain starts with version provided in genesis + infoResp = testApp.Info(abci.RequestInfo{}) + require.EqualValues(t, app.DefaultInitialConsensusParams().Version.AppVersion, infoResp.AppVersion) + + _ = testApp.Commit() + supportedVersions := []uint64{v1.Version, v2.Version} + require.Equal(t, supportedVersions, testApp.SupportedVersions()) + return testApp, kr +} diff --git a/x/minfee/upgrade_test.go b/x/minfee/upgrade_test.go index 7ce3566fdb..3fb5c02c84 100644 --- a/x/minfee/upgrade_test.go +++ b/x/minfee/upgrade_test.go @@ -89,7 +89,7 @@ func setupTestApp(t *testing.T, upgradeHeight int64) (*app.App, keyring.Keyring) db := dbm.NewMemDB() chainID := "test_chain" encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) - testApp := app.New(log.NewNopLogger(), db, nil, true, 0, encCfg, upgradeHeight, util.EmptyAppOptions{}) + testApp := app.New(log.NewNopLogger(), db, nil, 0, encCfg, upgradeHeight, util.EmptyAppOptions{}) genesisState, _, kr := util.GenesisStateWithSingleValidator(testApp, "account")