fix: app-hash mismatch if upgrade migration commit is interrupted (#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>
This commit is contained in:
co-authored by
yihuang
Aleksandr Bezobchuk
Amaury
parent
209797f3be
commit
b63adac6f4
@@ -175,6 +175,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
* (x/gov) [#13051](https://github.com/cosmos/cosmos-sdk/pull/13051) In SubmitPropsal, when a legacy msg fails it's handler call, wrap the error as ErrInvalidProposalContent (instead of ErrNoProposalHandlerExists).
|
||||
* (x/gov) [#13045](https://github.com/cosmos/cosmos-sdk/pull/13045) Fix gov migrations for v3(0.46).
|
||||
* (snapshot) [#13400](https://github.com/cosmos/cosmos-sdk/pull/13400) Fix snapshot checksum issue in golang 1.19.
|
||||
* (store) [#13530](https://github.com/cosmos/cosmos-sdk/pull/13530) Fix app-hash mismatch if upgrade migration commit is interrupted.
|
||||
|
||||
### Deprecated
|
||||
|
||||
|
||||
@@ -1040,8 +1040,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
|
||||
}
|
||||
|
||||
@@ -961,3 +961,76 @@ 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
|
||||
removalMap := map[types.StoreKey]bool{}
|
||||
res := commitStores(version, storeMap, removalMap)
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user