github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/params/upgrade_executor_test.go (about) 1 package params 2 3 import ( 4 "fmt" 5 "math" 6 "testing" 7 8 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store" 9 storetypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/types" 10 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 11 abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types" 12 "github.com/fibonacci-chain/fbc/libs/tendermint/libs/log" 13 tmdb "github.com/fibonacci-chain/fbc/libs/tm-db" 14 govtypes "github.com/fibonacci-chain/fbc/x/gov/types" 15 "github.com/fibonacci-chain/fbc/x/params/types" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/suite" 18 ) 19 20 type waitPair struct { 21 height uint64 22 proposalID uint64 23 } 24 type mockGovKeeper struct { 25 waitQueue []waitPair 26 proposals map[uint64]*govtypes.Proposal 27 28 handler govtypes.Handler 29 } 30 31 func newMockGovKeeper() *mockGovKeeper { 32 return &mockGovKeeper{ 33 handler: nil, 34 proposals: make(map[uint64]*govtypes.Proposal), 35 } 36 } 37 38 func (gk *mockGovKeeper) SetHandler(handler govtypes.Handler) { 39 gk.handler = handler 40 } 41 42 func (gk *mockGovKeeper) SetProposal(proposal *govtypes.Proposal) { 43 gk.proposals[proposal.ProposalID] = proposal 44 } 45 46 func (gk *mockGovKeeper) HitHeight(ctx sdk.Context, curHeight uint64, t *testing.T) sdk.Error { 47 var called []waitPair 48 defer func() { 49 for _, pair := range called { 50 gk.RemoveFromWaitingProposalQueue(ctx, pair.height, pair.proposalID) 51 } 52 }() 53 54 for _, pair := range gk.waitQueue { 55 if pair.height == curHeight { 56 proposal, ok := gk.proposals[pair.proposalID] 57 if !ok { 58 t.Fatalf("there's no proposal '%d' in mockGovKeeper", pair.proposalID) 59 } 60 called = append(called, pair) 61 62 if err := gk.handler(ctx, proposal); err != nil { 63 return err 64 } 65 } 66 } 67 68 if len(called) == 0 { 69 t.Fatalf("there's no proposal at height %d waiting to be handed", curHeight) 70 } 71 return nil 72 } 73 74 func (gk *mockGovKeeper) InsertWaitingProposalQueue(_ sdk.Context, blockHeight, proposalID uint64) { 75 gk.waitQueue = append(gk.waitQueue, waitPair{height: blockHeight, proposalID: proposalID}) 76 } 77 78 func (gk *mockGovKeeper) RemoveFromWaitingProposalQueue(_ sdk.Context, blockHeight, proposalID uint64) { 79 delIndex := -1 80 for i, pair := range gk.waitQueue { 81 if pair.height == blockHeight && pair.proposalID == proposalID { 82 delIndex = i 83 break 84 } 85 } 86 if delIndex < 0 { 87 return 88 } 89 gk.waitQueue = append(gk.waitQueue[:delIndex], gk.waitQueue[delIndex+1:]...) 90 } 91 92 func TestUpgradeProposalConfirmHeight(t *testing.T) { 93 tests := []struct { 94 currentHeight uint64 95 proposalExpectHeight uint64 96 expectError bool 97 expectConfirmHeight uint64 98 }{ 99 {uint64(10), uint64(0), false, uint64(10)}, 100 {uint64(10), uint64(5), true, uint64(0)}, 101 {uint64(10), uint64(10), true, uint64(0)}, 102 {uint64(10), uint64(11), false, uint64(10)}, 103 {uint64(10), uint64(15), false, uint64(14)}, 104 } 105 106 for _, tt := range tests { 107 proposal := types.NewUpgradeProposal("", "", "aa", tt.proposalExpectHeight, "") 108 confirmHeight, err := getUpgradeProposalConfirmHeight(tt.currentHeight, proposal) 109 if tt.expectError { 110 assert.Error(t, err) 111 continue 112 } 113 114 assert.NoError(t, err) 115 assert.Equal(t, tt.expectConfirmHeight, confirmHeight) 116 } 117 } 118 119 type UpgradeInfoStoreSuite struct { 120 suite.Suite 121 ms storetypes.CommitMultiStore 122 keeper Keeper 123 govKeeper *mockGovKeeper 124 } 125 126 func TestUpgradeInfoStore(t *testing.T) { 127 suite.Run(t, new(UpgradeInfoStoreSuite)) 128 } 129 130 func (suite *UpgradeInfoStoreSuite) SetupTest() { 131 db := tmdb.NewMemDB() 132 storeKey := sdk.NewKVStoreKey(StoreKey) 133 tstoreKey := sdk.NewTransientStoreKey(TStoreKey) 134 135 suite.ms = store.NewCommitMultiStore(tmdb.NewMemDB()) 136 suite.ms.MountStoreWithDB(storeKey, sdk.StoreTypeIAVL, db) 137 suite.ms.MountStoreWithDB(tstoreKey, sdk.StoreTypeTransient, db) 138 err := suite.ms.LoadLatestVersion() 139 suite.NoError(err) 140 141 suite.keeper = NewKeeper(ModuleCdc, storeKey, tstoreKey, log.NewNopLogger()) 142 143 suite.govKeeper = newMockGovKeeper() 144 suite.keeper.SetGovKeeper(suite.govKeeper) 145 } 146 147 func (suite *UpgradeInfoStoreSuite) Context(height int64) sdk.Context { 148 return sdk.NewContext(suite.ms, abci.Header{Height: height}, false, log.NewNopLogger()) 149 } 150 151 func (suite *UpgradeInfoStoreSuite) TestStoreUpgrade() { 152 tests := []struct { 153 storeFn func(sdk.Context, *Keeper, types.UpgradeProposal, uint64) sdk.Error 154 expectEffectiveHeight uint64 155 expectStatus types.UpgradeStatus 156 }{ 157 { 158 func(ctx sdk.Context, k *Keeper, upgrade types.UpgradeProposal, _ uint64) sdk.Error { 159 return storePreparingUpgrade(ctx, k, upgrade) 160 }, 161 0, 162 types.UpgradeStatusPreparing, 163 }, 164 { 165 storeWaitingUpgrade, 166 11, 167 types.UpgradeStatusWaitingEffective, 168 }, 169 { 170 func(ctx sdk.Context, k *Keeper, upgrade types.UpgradeProposal, h uint64) sdk.Error { 171 _, err := storeEffectiveUpgrade(ctx, k, upgrade, h) 172 return err 173 }, 174 22, 175 types.UpgradeStatusEffective, 176 }, 177 } 178 179 ctx := suite.Context(0) 180 for i, tt := range tests { 181 upgradeName := fmt.Sprintf("name %d", i) 182 183 expectInfo := types.UpgradeInfo{ 184 Name: upgradeName, 185 ExpectHeight: 111, 186 Config: "", 187 EffectiveHeight: 0, 188 Status: math.MaxUint32, 189 } 190 upgrade := types.NewUpgradeProposal(fmt.Sprintf("title-%d", i), fmt.Sprintf("desc-%d", i), expectInfo.Name, expectInfo.ExpectHeight, expectInfo.Config) 191 192 err := tt.storeFn(ctx, &suite.keeper, upgrade, tt.expectEffectiveHeight) 193 suite.NoError(err) 194 195 info, err := suite.keeper.readUpgradeInfo(ctx, upgradeName) 196 suite.NoError(err) 197 198 if tt.expectEffectiveHeight != 0 { 199 expectInfo.EffectiveHeight = tt.expectEffectiveHeight 200 } 201 expectInfo.Status = tt.expectStatus 202 suite.Equal(expectInfo, info) 203 } 204 } 205 206 func (suite *UpgradeInfoStoreSuite) TestStoreEffectiveUpgrade() { 207 const effectiveHeight = 111 208 209 ctx := suite.Context(10) 210 expectInfo := types.UpgradeInfo{ 211 Name: "abc", 212 ExpectHeight: 20, 213 EffectiveHeight: 22, 214 Status: types.UpgradeStatusPreparing, 215 } 216 217 upgrade := types.NewUpgradeProposal("ttt", "ddd", expectInfo.Name, expectInfo.ExpectHeight, expectInfo.Config) 218 info, err := storeEffectiveUpgrade(ctx, &suite.keeper, upgrade, effectiveHeight) 219 suite.NoError(err) 220 expectInfo.EffectiveHeight = effectiveHeight 221 expectInfo.Status = types.UpgradeStatusEffective 222 suite.Equal(expectInfo, info) 223 } 224 225 func (suite *UpgradeInfoStoreSuite) TestCheckUpgradeValidEffectiveHeight() { 226 tests := []struct { 227 effectiveHeight uint64 228 currentBlockHeight int64 229 maxBlockHeight uint64 230 expectError bool 231 }{ 232 {0, 111, 222, false}, 233 {9, 10, 222, true}, 234 {10, 10, 222, true}, 235 {11, 10, 222, false}, 236 {10 + 222 - 1, 10, 222, false}, 237 {10 + 222, 10, 222, false}, 238 {10 + 222 + 1, 10, 222, true}, 239 } 240 241 for _, tt := range tests { 242 ctx := suite.Context(tt.currentBlockHeight) 243 suite.keeper.SetParams(ctx, types.Params{MaxBlockHeight: tt.maxBlockHeight, MaxDepositPeriod: 10, VotingPeriod: 10}) 244 245 err := checkUpgradeValidEffectiveHeight(ctx, &suite.keeper, tt.effectiveHeight) 246 if tt.expectError { 247 suite.Error(err) 248 } else { 249 suite.NoError(err) 250 } 251 } 252 } 253 254 func (suite *UpgradeInfoStoreSuite) TestCheckUpgradeVote() { 255 tests := []struct { 256 expectHeight uint64 257 currentHeight int64 258 expectError bool 259 }{ 260 {0, 10, false}, 261 {0, 1111, false}, 262 {10, 11, true}, 263 {10, 10, true}, 264 {10, 9, false}, 265 } 266 267 for _, tt := range tests { 268 ctx := suite.Context(tt.currentHeight) 269 proposal := types.UpgradeProposal{ExpectHeight: tt.expectHeight} 270 271 _, err := checkUpgradeVote(ctx, 0, proposal, govtypes.Vote{}) 272 if tt.expectError { 273 suite.Error(err) 274 } else { 275 suite.NoError(err) 276 } 277 } 278 } 279 280 func (suite *UpgradeInfoStoreSuite) TestHandleUpgradeProposal() { 281 tests := []struct { 282 expectHeight uint64 283 currentHeight uint64 284 claimReady bool 285 expectPanic bool 286 expect1stExecuteError bool 287 expectHitError bool 288 }{ 289 { // expect height is not zero but less than current height 290 expectHeight: 10, currentHeight: 10, claimReady: false, expectPanic: false, expect1stExecuteError: true, 291 }, 292 { // expect height is not zero but only greater than current height 1; and not claim ready 293 expectHeight: 11, currentHeight: 10, claimReady: false, expectPanic: true, 294 }, 295 { // expect height is not zero and greater than current height; but not claim ready 296 expectHeight: 12, currentHeight: 10, claimReady: false, expectPanic: true, expect1stExecuteError: false, 297 }, 298 { // everything's ok: expect height is not zero and greater than current height; and claim ready 299 expectHeight: 12, currentHeight: 10, claimReady: true, expectPanic: false, expect1stExecuteError: false, expectHitError: false, 300 }, 301 { // everything's ok: expect height is zero and claim ready 302 expectHeight: 0, currentHeight: 10, claimReady: true, expectPanic: false, expect1stExecuteError: false, expectHitError: false, 303 }, 304 } 305 306 handler := NewUpgradeProposalHandler(&suite.keeper) 307 suite.govKeeper.SetHandler(handler) 308 309 for i, tt := range tests { 310 ctx := suite.Context(int64(tt.currentHeight)) 311 upgradeProposal := types.NewUpgradeProposal("title", "desc", fmt.Sprintf("name-%d", i), tt.expectHeight, "") 312 proposal := &govtypes.Proposal{Content: upgradeProposal, ProposalID: uint64(i)} 313 suite.govKeeper.SetProposal(proposal) 314 315 confirmHeight := tt.expectHeight - 1 316 if tt.expectHeight == 0 { 317 confirmHeight = tt.currentHeight 318 } 319 effectiveHeight := confirmHeight + 1 320 321 cbCount := 0 322 cbName := "" 323 if tt.claimReady { 324 suite.keeper.ClaimReadyForUpgrade(upgradeProposal.Name, func(info types.UpgradeInfo) { 325 cbName = info.Name 326 cbCount += 1 327 }) 328 } 329 330 if tt.expectPanic && confirmHeight == tt.currentHeight { 331 suite.Panics(func() { _ = handler(ctx, proposal) }) 332 continue 333 } 334 335 // execute proposal 336 err := handler(ctx, proposal) 337 if tt.expect1stExecuteError { 338 suite.Error(err) 339 continue 340 } 341 342 suite.GreaterOrEqual(confirmHeight, tt.currentHeight) 343 if confirmHeight != tt.currentHeight { 344 // proposal is inserted to gov waiting queue, execute it 345 expectInfo := types.UpgradeInfo{ 346 Name: upgradeProposal.Name, 347 ExpectHeight: upgradeProposal.ExpectHeight, 348 Config: upgradeProposal.Config, 349 EffectiveHeight: effectiveHeight, 350 Status: types.UpgradeStatusWaitingEffective, 351 } 352 info, err := suite.keeper.readUpgradeInfo(ctx, upgradeProposal.Name) 353 suite.NoError(err) 354 suite.Equal(expectInfo, info) 355 356 ctx := suite.Context(int64(confirmHeight)) 357 if tt.expectPanic { 358 suite.Panics(func() { _ = suite.govKeeper.HitHeight(ctx, confirmHeight, suite.T()) }) 359 continue 360 } 361 err = suite.govKeeper.HitHeight(ctx, confirmHeight, suite.T()) 362 if tt.expectHitError { 363 suite.Error(err) 364 continue 365 } 366 suite.NoError(err) 367 } 368 369 // now proposal must be executed 370 expectInfo := types.UpgradeInfo{ 371 Name: upgradeProposal.Name, 372 ExpectHeight: upgradeProposal.ExpectHeight, 373 Config: upgradeProposal.Config, 374 EffectiveHeight: effectiveHeight, 375 Status: types.UpgradeStatusEffective, 376 } 377 info, err := suite.keeper.readUpgradeInfo(ctx, upgradeProposal.Name) 378 suite.NoError(err) 379 suite.Equal(expectInfo, info) 380 381 suite.Equal(upgradeProposal.Name, cbName) 382 suite.Equal(1, cbCount) 383 } 384 }