Skip to content

Commit

Permalink
fix: app-hash mismatch if upgrade migration commit is interrupted (co…
Browse files Browse the repository at this point in the history
…smos#13530)

* avoid append commit when upgrade get interrupted

* add commitStores test

* add change doc

* Update store/rootmulti/store.go

Co-authored-by: yihuang <huang@crypto.com>
Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>
Co-authored-by: Amaury <1293565+amaurym@users.noreply.github.com>
  • Loading branch information
4 people authored and SpicyLemon committed Oct 26, 2022
1 parent 2820075 commit df4d9ff
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 2 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Expand Up @@ -39,7 +39,11 @@ Ref: https://keepachangelog.com/en/1.0.0/

## Unreleased

* nothing
### Bug Fixes

* (store) [#13530](https://github.com/cosmos/cosmos-sdk/pull/13530) Fix app-hash mismatch if upgrade migration commit is interrupted.

---

## [v0.45.10-pio-1](https://github.com/provenance-io/cosmos-sdk/releases/tag/v0.45.10-pio-1) - 2022-10-24

Expand Down
10 changes: 9 additions & 1 deletion store/rootmulti/store.go
Expand Up @@ -976,8 +976,16 @@ func commitStores(version int64, storeMap map[types.StoreKey]types.CommitKVStore
storeInfos := make([]types.StoreInfo, 0, len(storeMap))

for key, store := range storeMap {
commitID := store.Commit()
last := store.LastCommitID()

// If a commit event execution is interrupted, a new iavl store's version will be larger than the rootmulti's metadata, when the block is replayed, we should avoid committing that iavl store again.
var commitID types.CommitID
if last.Version >= version {
last.Version = version
commitID = last
} else {
commitID = store.Commit()
}
if store.GetStoreType() == types.StoreTypeTransient {
continue
}
Expand Down
72 changes: 72 additions & 0 deletions store/rootmulti/store_test.go
Expand Up @@ -882,3 +882,75 @@ func TestStateListeners(t *testing.T) {
cacheMulti.Write()
require.Equal(t, 1, len(listener.stateCache))
}

type commitKVStoreStub struct {
types.CommitKVStore
Committed int
}

func (stub *commitKVStoreStub) Commit() types.CommitID {
commitID := stub.CommitKVStore.Commit()
stub.Committed += 1
return commitID
}

func prepareStoreMap() map[types.StoreKey]types.CommitKVStore {
var db dbm.DB = dbm.NewMemDB()
store := NewStore(db, log.NewNopLogger())
store.MountStoreWithDB(types.NewKVStoreKey("iavl1"), types.StoreTypeIAVL, nil)
store.MountStoreWithDB(types.NewKVStoreKey("iavl2"), types.StoreTypeIAVL, nil)
store.MountStoreWithDB(types.NewTransientStoreKey("trans1"), types.StoreTypeTransient, nil)
store.LoadLatestVersion()
return map[types.StoreKey]types.CommitKVStore{
testStoreKey1: &commitKVStoreStub{
CommitKVStore: store.GetStoreByName("iavl1").(types.CommitKVStore),
},
testStoreKey2: &commitKVStoreStub{
CommitKVStore: store.GetStoreByName("iavl2").(types.CommitKVStore),
},
testStoreKey3: &commitKVStoreStub{
CommitKVStore: store.GetStoreByName("trans1").(types.CommitKVStore),
},
}
}

func TestCommitStores(t *testing.T) {
testCases := []struct {
name string
committed int
exptectCommit int
}{
{
"when upgrade not get interrupted",
0,
1,
},
{
"when upgrade get interrupted once",
1,
0,
},
{
"when upgrade get interrupted twice",
2,
0,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
storeMap := prepareStoreMap()
store := storeMap[testStoreKey1].(*commitKVStoreStub)
for i := tc.committed; i > 0; i-- {
store.Commit()
}
store.Committed = 0
var version int64 = 1
res := commitStores(version, storeMap)
for _, s := range res.StoreInfos {
require.Equal(t, version, s.CommitId.Version)
}
require.Equal(t, version, res.Version)
require.Equal(t, tc.exptectCommit, store.Committed)
})
}
}

0 comments on commit df4d9ff

Please sign in to comment.