From 28bb1480f198eef2434d3bb0c75facc7b2b767ac Mon Sep 17 00:00:00 2001 From: Haiyi Zhong Date: Tue, 5 Nov 2024 17:02:41 -0500 Subject: [PATCH] feat(app)!: migrate pre-initialized module accounts --- app/app.go | 14 ++---- app/migrate_module_account.go | 78 ++++++++++++++++++++++++++++++ app/migrate_module_account_test.go | 63 ++++++++++++++++++++++++ 3 files changed, 146 insertions(+), 9 deletions(-) create mode 100644 app/migrate_module_account.go create mode 100644 app/migrate_module_account_test.go diff --git a/app/app.go b/app/app.go index 1ac1367c4..e99444731 100644 --- a/app/app.go +++ b/app/app.go @@ -477,18 +477,14 @@ func (app *AxelarApp) setUpgradeBehaviour(configurator module.Configurator, keep upgradeKeeper.SetUpgradeHandler( upgradeName(app.Version()), func(ctx sdk.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { - updatedVM, err := app.mm.RunMigrations(ctx, configurator, fromVM) + err := MigratePreInitializedModuleAccounts(ctx, *GetKeeper[authkeeper.AccountKeeper](keepers), []string{nexusTypes.ModuleName}) if err != nil { - return updatedVM, err + return nil, err } - // TODO: remove after v35 upgrade - // Override wasm module default params - if upgradeName(app.Version()) == "v0.35" && IsWasmEnabled() { - GetKeeper[wasm.Keeper](keepers).SetParams(ctx, wasmtypes.Params{ - CodeUploadAccess: wasmtypes.AllowNobody, - InstantiateDefaultPermission: wasmtypes.AccessTypeNobody, - }) + updatedVM, err := app.mm.RunMigrations(ctx, configurator, fromVM) + if err != nil { + return updatedVM, err } return updatedVM, err diff --git a/app/migrate_module_account.go b/app/migrate_module_account.go new file mode 100644 index 000000000..1a5d5293e --- /dev/null +++ b/app/migrate_module_account.go @@ -0,0 +1,78 @@ +package app + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +// MigratePreInitializedModuleAccounts migrates module accounts that were pre-initialized as BaseAccounts to ModuleAccounts, +// or creates new module accounts if they don't exist. +func MigratePreInitializedModuleAccounts( + ctx sdk.Context, + ak authkeeper.AccountKeeper, + moduleAccountsToInitialize []string, +) error { + for _, module := range moduleAccountsToInitialize { + addr, perms := ak.GetModuleAddressAndPermissions(module) + if addr == nil { + return fmt.Errorf( + "failed to get module address and permissions for module %s", + module, + ) + } + + acc := ak.GetAccount(ctx, addr) + // The account has not been initialized yet + if acc == nil { + initModuleAccount(ctx, ak, module, perms...) + continue + } + + _, isModuleAccount := acc.(authtypes.ModuleAccountI) + if isModuleAccount { + ctx.Logger().Info(fmt.Sprintf( + "account for module %s was correctly initialized, skipping", + module, + )) + continue + } + + // Migrate from base account to module account + baseAccount, ok := acc.(*authtypes.BaseAccount) + if !ok { + panic(fmt.Sprintf("account %s must be a base account", acc.GetAddress())) + } + + newModuleAccount := authtypes.NewModuleAccount( + baseAccount, + module, + perms..., + ) + ak.SetModuleAccount(ctx, newModuleAccount) + + ctx.Logger().Info(fmt.Sprintf( + "migrated %s module from base account %+v to module account %+v", + module, + baseAccount, + newModuleAccount, + )) + } + + return nil +} + +// create a new module account +func initModuleAccount(ctx sdk.Context, ak authkeeper.AccountKeeper, moduleName string, perms ...string) { + newModuleAccount := authtypes.NewEmptyModuleAccount(moduleName, perms...) + maccI := (ak.NewAccount(ctx, newModuleAccount)).(authtypes.ModuleAccountI) // set the account number + ak.SetModuleAccount(ctx, maccI) + + ctx.Logger().Info(fmt.Sprintf( + "initialized %s module account %+v", + moduleName, + newModuleAccount, + )) +} diff --git a/app/migrate_module_account_test.go b/app/migrate_module_account_test.go new file mode 100644 index 000000000..afe45b468 --- /dev/null +++ b/app/migrate_module_account_test.go @@ -0,0 +1,63 @@ +package app_test + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + params "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/axelarnetwork/axelar-core/app" + "github.com/axelarnetwork/axelar-core/testutils/fake" + nexusTypes "github.com/axelarnetwork/axelar-core/x/nexus/types" + . "github.com/axelarnetwork/utils/test" +) + +func TestMigratePreInitializedModuleAccounts(t *testing.T) { + var ( + accountK authkeeper.AccountKeeper + ctx sdk.Context + ) + + Given("an account keeper", func() { + encodingConfig := app.MakeEncodingConfig() + ctx = sdk.NewContext(fake.NewMultiStore(), tmproto.Header{}, false, log.TestingLogger()) + storeKey := sdk.NewKVStoreKey(authtypes.StoreKey) + + moduleAccPerms := map[string][]string{ + "module1": nil, + nexusTypes.ModuleName: {authtypes.Minter, authtypes.Burner}, + } + + accountK = authkeeper.NewAccountKeeper( + encodingConfig.Codec, + sdk.NewKVStoreKey(authtypes.StoreKey), + params.NewSubspace(encodingConfig.Codec, encodingConfig.Amino, storeKey, storeKey, authtypes.ModuleName), + authtypes.ProtoBaseAccount, + moduleAccPerms, + ) + }).When("there is an pre-initialized module account", func() { + account := accountK.NewAccountWithAddress(ctx, authtypes.NewModuleAddress(nexusTypes.ModuleName)) + accountK.SetAccount(ctx, account) + + account = accountK.GetAccount(ctx, authtypes.NewModuleAddress(nexusTypes.ModuleName)) + _, isModuleAccount := account.(authtypes.ModuleAccountI) + assert.False(t, isModuleAccount) + + }).Then("migrating pre-initialized base account to module account", func(t *testing.T) { + err := app.MigratePreInitializedModuleAccounts(ctx, accountK, []string{"module1", nexusTypes.ModuleName}) + assert.NoError(t, err) + + account := accountK.GetAccount(ctx, authtypes.NewModuleAddress(nexusTypes.ModuleName)) + _, isModuleAccount := account.(authtypes.ModuleAccountI) + assert.True(t, isModuleAccount) + + account = accountK.GetAccount(ctx, authtypes.NewModuleAddress("module1")) + _, isModuleAccount = account.(authtypes.ModuleAccountI) + assert.True(t, isModuleAccount) + }).Run(t) +}