Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: add migrator to the commit abci call #387

Merged
merged 4 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions baseapp/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,36 @@ func (app *BaseApp) Commit() abci.ResponseCommit {
// The write to the DeliverTx state writes all state transitions to the root
// MultiStore (app.cms) so when Commit() is called is persists those values.
app.deliverState.ms.Write()

// Check if there has been an app version change. If so and there is a migrator
// set, begin to run migrations. This needs to be done before the commit so
// that the migrations are part of the app hash
if header.Version.App < app.appVersion &&
app.migrator.storeMigrator != nil &&
app.migrator.moduleMigrator != nil {

// first update the stores themselves by adding and removing them as necessary
storeMigrations, err := app.migrator.storeMigrator(header.Version.App, app.appVersion)
if err != nil {
panic(fmt.Sprintf("failed to get store migrations: %v", err))
}
app.MountKVStores(storeMigrations.Added)
err = app.cms.LoadLatestVersionAndUpgrade(storeMigrations.ToStoreUpgrades())
if err != nil {
panic(fmt.Sprintf("failed to upgrade stores: %v", err))
}

// create a new cached branch of the store to apply migrations to
app.setDeliverState(header)
err = app.migrator.moduleMigrator(app.deliverState.ctx, header.Version.App, app.appVersion)
if err != nil {
panic(fmt.Sprintf("failed to migrate modules: %v", err))
}
Comment on lines +355 to +370
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to be careful about atomicity here. I need to double check how CometBFT would handle this panic but iirc, we should also revert all the transactions previously committed


// write the new state to the branch
app.deliverState.ms.Write()
}

commitID := app.cms.Commit()

res := abci.ResponseCommit{
Expand Down
21 changes: 21 additions & 0 deletions baseapp/baseapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,21 @@ type (
// an older version of the software. In particular, if a module changed the substore key name
// (or removed a substore) between two versions of the software.
StoreLoader func(ms sdk.CommitMultiStore) error

// MigrateModuleFn gets called when there is a change in the app version. It is
// triggered in the Commit stage after all state transitions have occurred.
MigrateModuleFn func(ctx sdk.Context, fromVersion, toVersion uint64) error

// MigrateStoreFn gets called when there is a change in the app version. It is
// triggered in the Commit stage after all state transitions have occurred.
MigrateStoreFn func(fromVersion, toVersion uint64) (StoreMigrations, error)

// StoreMigrations is a type that contains the added and removed stores for a
// migration.
StoreMigrations struct {
Added map[string]*storetypes.KVStoreKey
Deleted map[string]*storetypes.KVStoreKey
}
)

// BaseApp reflects the ABCI application implementation.
Expand All @@ -57,6 +72,7 @@ type BaseApp struct { // nolint: maligned
snapshotData
abciData
moduleRouter
migrator

// volatile states:
//
Expand Down Expand Up @@ -135,6 +151,11 @@ type appStore struct {
fauxMerkleMode bool // if true, IAVL MountStores uses MountStoresDB for simulation speed.
}

type migrator struct {
moduleMigrator MigrateModuleFn
storeMigrator MigrateStoreFn
}

type moduleRouter struct {
router sdk.Router // handle any kind of message
queryRouter sdk.QueryRouter // router for redirecting query calls
Expand Down
39 changes: 39 additions & 0 deletions baseapp/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import (
"fmt"
"io"
"sort"

tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
dbm "github.com/tendermint/tm-db"
Expand All @@ -12,6 +13,7 @@
"github.com/cosmos/cosmos-sdk/snapshots"
snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types"
"github.com/cosmos/cosmos-sdk/store"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)

Expand Down Expand Up @@ -160,6 +162,20 @@
app.endBlocker = endBlocker
}

func (app *BaseApp) SetMigrateModuleFn(migrator MigrateModuleFn) {
if app.sealed {
panic("cannot set migrate module fn: baseapp is sealed")
}
app.migrator.moduleMigrator = migrator
}

func (app *BaseApp) SetMigrateStoreFn(migrator MigrateStoreFn) {
if app.sealed {
panic("cannot set migrate store fn: baseapp is sealed")
}
app.migrator.storeMigrator = migrator
}

func (app *BaseApp) SetAnteHandler(ah sdk.AnteHandler) {
if app.sealed {
panic("SetAnteHandler() on sealed BaseApp")
Expand Down Expand Up @@ -263,3 +279,26 @@
}
app.qms = ms
}

// ToStoreUpgrades converts the StoreMigrations to StoreUpgrades.
func (sm StoreMigrations) ToStoreUpgrades() *storetypes.StoreUpgrades {
added := make([]string, len(sm.Added))
deleted := make([]string, len(sm.Deleted))
i := 0
for name := range sm.Added {
added[i] = name
i++
}
Fixed Show fixed Hide fixed
Comment on lines +288 to +291

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
i = 0
for name := range sm.Deleted {
deleted[i] = name
i++
}
Fixed Show fixed Hide fixed
Comment on lines +293 to +296

Check warning

Code scanning / CodeQL

Iteration over map Warning

Iteration over map may be a possible source of non-determinism
// sort them to ensure deterministic order
sort.Strings(added)
sort.Strings(deleted)
return &storetypes.StoreUpgrades{
Added: added,
Deleted: deleted,
}
}
Loading