From b822b3c8a20736076166f6d4e8254c670ca7bd57 Mon Sep 17 00:00:00 2001 From: Alex Gartner Date: Thu, 2 May 2024 08:50:42 -0700 Subject: [PATCH] refactor/comment/panic --- app/setup_handlers.go | 94 ++---------------- app/upgrade_tracker.go | 95 +++++++++++++++++++ ...ndlers_test.go => upgrade_tracker_test.go} | 38 ++++---- 3 files changed, 121 insertions(+), 106 deletions(-) create mode 100644 app/upgrade_tracker.go rename app/{setup_handlers_test.go => upgrade_tracker_test.go} (66%) diff --git a/app/setup_handlers.go b/app/setup_handlers.go index fe3e06f055..7a194145e8 100644 --- a/app/setup_handlers.go +++ b/app/setup_handlers.go @@ -1,10 +1,6 @@ package app import ( - "fmt" - "os" - "path" - "strconv" "strings" storetypes "github.com/cosmos/cosmos-sdk/store/types" @@ -20,17 +16,17 @@ const releaseVersion = "v15" func SetupHandlers(app *App) { // add each upgrade to this slice with a monotonically increasing index. - // you must use a index that is greater than any migration that has ever been run. + // the easiest index to use is the current unix epoch timestamp. allUpgrades := upgradeTracker{ upgrades: []upgradeTrackerItem{ { - idx: 1000, + index: 1713000000, storeUpgrade: &storetypes.StoreUpgrades{ Added: []string{authoritytypes.ModuleName}, }, }, { - idx: 2000, + index: 1714664193, storeUpgrade: &storetypes.StoreUpgrades{ Added: []string{lightclienttypes.ModuleName}, }, @@ -40,7 +36,10 @@ func SetupHandlers(app *App) { } isDevelopBuild := strings.Contains(releaseVersion, "develop") - versionModifierFns, storeUpgrades := allUpgrades.getUpgrades(isDevelopBuild) + versionModifierFns, storeUpgrades, err := allUpgrades.getUpgrades(isDevelopBuild) + if err != nil { + panic(err) + } app.UpgradeKeeper.SetUpgradeHandler(releaseVersion, func(ctx sdk.Context, _ types.Plan, vm module.VersionMap) (module.VersionMap, error) { app.Logger().Info("Running upgrade handler for " + releaseVersion) @@ -68,82 +67,3 @@ func SetupHandlers(app *App) { app.SetStoreLoader(types.UpgradeStoreLoader(upgradeInfo.Height, storeUpgrades)) } } - -type versionModiferFn func(ctx sdk.Context, vm module.VersionMap) (module.VersionMap, error) - -type upgradeTrackerItem struct { - idx int64 - versionModifier versionModiferFn - storeUpgrade *storetypes.StoreUpgrades -} - -type upgradeTracker struct { - upgrades []upgradeTrackerItem - stateFileDir string -} - -func (t upgradeTracker) getDevelopUpgrades() ([]versionModiferFn, *storetypes.StoreUpgrades) { - neededUpgrades := &storetypes.StoreUpgrades{} - neededVersionModifiers := []versionModiferFn{} - stateFilePath := path.Join(t.stateFileDir, "developupgradetracker") - - currentIdx := int64(0) - // #nosec G304 stateFilePath is not user controllable - if stateFileContents, err := os.ReadFile(stateFilePath); err == nil { - currentIdx, err = strconv.ParseInt(string(stateFileContents), 10, 64) - if err != nil { - panic("unable to decode store upgrade tracker") - } - } else { - fmt.Printf("unable to load store upgrade tracker: %v\n", err) - } - - maxIdx := currentIdx - for _, item := range t.upgrades { - idx := item.idx - upgrade := item.storeUpgrade - versionModifier := item.versionModifier - if idx <= currentIdx { - continue - } - if versionModifier != nil { - neededVersionModifiers = append(neededVersionModifiers, versionModifier) - } - if upgrade != nil { - neededUpgrades.Added = append(neededUpgrades.Added, upgrade.Added...) - neededUpgrades.Deleted = append(neededUpgrades.Deleted, upgrade.Deleted...) - neededUpgrades.Renamed = append(neededUpgrades.Renamed, upgrade.Renamed...) - } - maxIdx = idx - } - err := os.WriteFile(stateFilePath, []byte(strconv.FormatInt(maxIdx, 10)), 0o600) - if err != nil { - panic(fmt.Sprintf("unable to write upgrade state file: %v", err)) - } - return neededVersionModifiers, neededUpgrades -} - -func (t upgradeTracker) mergeAllUpgrades() ([]versionModiferFn, *storetypes.StoreUpgrades) { - upgrades := &storetypes.StoreUpgrades{} - versionModifiers := []versionModiferFn{} - for _, item := range t.upgrades { - upgrade := item.storeUpgrade - versionModifier := item.versionModifier - if versionModifier != nil { - versionModifiers = append(versionModifiers, versionModifier) - } - if upgrade != nil { - upgrades.Added = append(upgrades.Added, upgrade.Added...) - upgrades.Deleted = append(upgrades.Deleted, upgrade.Deleted...) - upgrades.Renamed = append(upgrades.Renamed, upgrade.Renamed...) - } - } - return versionModifiers, upgrades -} - -func (t upgradeTracker) getUpgrades(isDevelop bool) ([]versionModiferFn, *storetypes.StoreUpgrades) { - if isDevelop { - return t.getDevelopUpgrades() - } - return t.mergeAllUpgrades() -} diff --git a/app/upgrade_tracker.go b/app/upgrade_tracker.go new file mode 100644 index 0000000000..29f27a7a6e --- /dev/null +++ b/app/upgrade_tracker.go @@ -0,0 +1,95 @@ +package app + +import ( + "fmt" + "os" + "path" + "strconv" + + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" +) + +type upgradeHandlerFn func(ctx sdk.Context, vm module.VersionMap) (module.VersionMap, error) + +type upgradeTrackerItem struct { + // Monotonically increasing index to order and track migrations. Typically the current unix epoch timestamp. + index int64 + // Function that will run during the SetUpgradeHandler callback. The VersionMap must always be returned. + upgradeHandler upgradeHandlerFn + // StoreUpgrades that will be provided to UpgradeStoreLoader + storeUpgrade *storetypes.StoreUpgrades +} + +// upgradeTracker allows us to track needed upgrades/migrations across both release and develop builds +type upgradeTracker struct { + upgrades []upgradeTrackerItem + stateFileDir string +} + +func (t upgradeTracker) getDevelopUpgrades() ([]upgradeHandlerFn, *storetypes.StoreUpgrades, error) { + neededUpgrades := &storetypes.StoreUpgrades{} + neededUpgradeHandlers := []upgradeHandlerFn{} + stateFilePath := path.Join(t.stateFileDir, "developupgradetracker") + + currentIndex := int64(0) + // #nosec G304 stateFilePath is not user controllable + if stateFileContents, err := os.ReadFile(stateFilePath); err == nil { + currentIndex, err = strconv.ParseInt(string(stateFileContents), 10, 64) + if err != nil { + return nil, nil, fmt.Errorf("unable to decode upgrade tracker: %w", err) + } + } else { + fmt.Printf("unable to load upgrade tracker: %v\n", err) + } + + maxIndex := currentIndex + for _, item := range t.upgrades { + index := item.index + upgrade := item.storeUpgrade + versionModifier := item.upgradeHandler + if index <= currentIndex { + continue + } + if versionModifier != nil { + neededUpgradeHandlers = append(neededUpgradeHandlers, versionModifier) + } + if upgrade != nil { + neededUpgrades.Added = append(neededUpgrades.Added, upgrade.Added...) + neededUpgrades.Deleted = append(neededUpgrades.Deleted, upgrade.Deleted...) + neededUpgrades.Renamed = append(neededUpgrades.Renamed, upgrade.Renamed...) + } + maxIndex = index + } + err := os.WriteFile(stateFilePath, []byte(strconv.FormatInt(maxIndex, 10)), 0o600) + if err != nil { + return nil, nil, fmt.Errorf("unable to write upgrade state file: %w", err) + } + return neededUpgradeHandlers, neededUpgrades, nil +} + +func (t upgradeTracker) mergeAllUpgrades() ([]upgradeHandlerFn, *storetypes.StoreUpgrades, error) { + upgrades := &storetypes.StoreUpgrades{} + upgradeHandlers := []upgradeHandlerFn{} + for _, item := range t.upgrades { + upgrade := item.storeUpgrade + versionModifier := item.upgradeHandler + if versionModifier != nil { + upgradeHandlers = append(upgradeHandlers, versionModifier) + } + if upgrade != nil { + upgrades.Added = append(upgrades.Added, upgrade.Added...) + upgrades.Deleted = append(upgrades.Deleted, upgrade.Deleted...) + upgrades.Renamed = append(upgrades.Renamed, upgrade.Renamed...) + } + } + return upgradeHandlers, upgrades, nil +} + +func (t upgradeTracker) getUpgrades(isDevelop bool) ([]upgradeHandlerFn, *storetypes.StoreUpgrades, error) { + if isDevelop { + return t.getDevelopUpgrades() + } + return t.mergeAllUpgrades() +} diff --git a/app/setup_handlers_test.go b/app/upgrade_tracker_test.go similarity index 66% rename from app/setup_handlers_test.go rename to app/upgrade_tracker_test.go index 573b58b45d..c8138d452b 100644 --- a/app/setup_handlers_test.go +++ b/app/upgrade_tracker_test.go @@ -3,7 +3,6 @@ package app import ( "os" "testing" - "time" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -13,35 +12,32 @@ import ( lightclienttypes "github.com/zeta-chain/zetacore/x/lightclient/types" ) -func TestStoreUpgradeTracker(t *testing.T) { +func TestUpgradeTracker(t *testing.T) { r := require.New(t) - // use timestamp for tracker idx so migrations work regardless of system state - now := time.Now().UnixMicro() - tmpdir, err := os.MkdirTemp("", "storeupgradetracker-*") r.NoError(err) allUpgrades := upgradeTracker{ upgrades: []upgradeTrackerItem{ { - idx: 1000, + index: 1000, storeUpgrade: &storetypes.StoreUpgrades{ Added: []string{authoritytypes.ModuleName}, }, }, { - idx: 2000, + index: 2000, storeUpgrade: &storetypes.StoreUpgrades{ Added: []string{lightclienttypes.ModuleName}, }, - versionModifier: func(ctx sdk.Context, vm module.VersionMap) (module.VersionMap, error) { + upgradeHandler: func(ctx sdk.Context, vm module.VersionMap) (module.VersionMap, error) { return vm, nil }, }, { - idx: 3000, - versionModifier: func(ctx sdk.Context, vm module.VersionMap) (module.VersionMap, error) { + index: 3000, + upgradeHandler: func(ctx sdk.Context, vm module.VersionMap) (module.VersionMap, error) { return vm, nil }, }, @@ -49,38 +45,42 @@ func TestStoreUpgradeTracker(t *testing.T) { stateFileDir: tmpdir, } - versionModifierFns, storeUpgrades := allUpgrades.mergeAllUpgrades() + upgradeHandlers, storeUpgrades, err := allUpgrades.mergeAllUpgrades() + r.NoError(err) r.Len(storeUpgrades.Added, 2) r.Len(storeUpgrades.Renamed, 0) r.Len(storeUpgrades.Deleted, 0) - r.Len(versionModifierFns, 2) + r.Len(upgradeHandlers, 2) // should return all migrations on first call - versionModifierFns, storeUpgrades = allUpgrades.getDevelopUpgrades() + upgradeHandlers, storeUpgrades, err = allUpgrades.getDevelopUpgrades() + r.NoError(err) r.Len(storeUpgrades.Added, 2) r.Len(storeUpgrades.Renamed, 0) r.Len(storeUpgrades.Deleted, 0) - r.Len(versionModifierFns, 2) + r.Len(upgradeHandlers, 2) // should return no upgrades on second call - versionModifierFns, storeUpgrades = allUpgrades.getDevelopUpgrades() + upgradeHandlers, storeUpgrades, err = allUpgrades.getDevelopUpgrades() + r.NoError(err) r.Len(storeUpgrades.Added, 0) r.Len(storeUpgrades.Renamed, 0) r.Len(storeUpgrades.Deleted, 0) - r.Len(versionModifierFns, 0) + r.Len(upgradeHandlers, 0) // now add a upgrade and ensure that it gets run without running // the other upgrades allUpgrades.upgrades = append(allUpgrades.upgrades, upgradeTrackerItem{ - idx: now + 3, + index: 4000, storeUpgrade: &storetypes.StoreUpgrades{ Deleted: []string{"example"}, }, }) - versionModifierFns, storeUpgrades = allUpgrades.getDevelopUpgrades() + upgradeHandlers, storeUpgrades, err = allUpgrades.getDevelopUpgrades() + r.NoError(err) r.Len(storeUpgrades.Added, 0) r.Len(storeUpgrades.Renamed, 0) r.Len(storeUpgrades.Deleted, 1) - r.Len(versionModifierFns, 0) + r.Len(upgradeHandlers, 0) }