github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/x/upgrade/abci_test.go (about) 1 package upgrade_test 2 3 import ( 4 "errors" 5 "testing" 6 "time" 7 8 abci "github.com/fibonacci-chain/fbc/libs/tendermint/abci/types" 9 "github.com/fibonacci-chain/fbc/libs/tendermint/libs/log" 10 dbm "github.com/fibonacci-chain/fbc/libs/tm-db" 11 "github.com/stretchr/testify/require" 12 13 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec" 14 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/simapp" 15 sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types" 16 sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors" 17 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/module" 18 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/gov" 19 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/upgrade" 20 ) 21 22 type TestSuite struct { 23 module module.AppModule 24 keeper upgrade.Keeper 25 querier sdk.Querier 26 handler gov.Handler 27 ctx sdk.Context 28 } 29 30 var s TestSuite 31 32 func setupTest(height int64, skip map[int64]bool) TestSuite { 33 db := dbm.NewMemDB() 34 app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, skip, 0) 35 genesisState := simapp.NewDefaultGenesisState() 36 stateBytes, err := codec.MarshalJSONIndent(app.Codec(), genesisState) 37 if err != nil { 38 panic(err) 39 } 40 app.InitChain( 41 abci.RequestInitChain{ 42 Validators: []abci.ValidatorUpdate{}, 43 AppStateBytes: stateBytes, 44 }, 45 ) 46 47 s.keeper = app.UpgradeKeeper 48 s.ctx = app.BaseApp.NewContext(false, abci.Header{Height: height, Time: time.Now()}) 49 50 s.module = upgrade.NewAppModule(s.keeper) 51 s.querier = s.module.NewQuerierHandler() 52 s.handler = upgrade.NewSoftwareUpgradeProposalHandler(s.keeper) 53 return s 54 } 55 56 func TestRequireName(t *testing.T) { 57 s := setupTest(10, map[int64]bool{}) 58 59 err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{}}) 60 require.NotNil(t, err) 61 require.True(t, errors.Is(sdkerrors.ErrInvalidRequest, err), err) 62 } 63 64 func TestRequireFutureTime(t *testing.T) { 65 s := setupTest(10, map[int64]bool{}) 66 err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Time: s.ctx.BlockHeader().Time}}) 67 require.NotNil(t, err) 68 require.True(t, errors.Is(sdkerrors.ErrInvalidRequest, err), err) 69 } 70 71 func TestRequireFutureBlock(t *testing.T) { 72 s := setupTest(10, map[int64]bool{}) 73 err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Height: s.ctx.BlockHeight()}}) 74 require.NotNil(t, err) 75 require.True(t, errors.Is(sdkerrors.ErrInvalidRequest, err), err) 76 } 77 78 func TestCantSetBothTimeAndHeight(t *testing.T) { 79 s := setupTest(10, map[int64]bool{}) 80 err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Time: time.Now(), Height: s.ctx.BlockHeight() + 1}}) 81 require.NotNil(t, err) 82 require.True(t, errors.Is(sdkerrors.ErrInvalidRequest, err), err) 83 } 84 85 func TestDoTimeUpgrade(t *testing.T) { 86 s := setupTest(10, map[int64]bool{}) 87 t.Log("Verify can schedule an upgrade") 88 err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Time: time.Now()}}) 89 require.Nil(t, err) 90 91 VerifyDoUpgrade(t) 92 } 93 94 func TestDoHeightUpgrade(t *testing.T) { 95 s := setupTest(10, map[int64]bool{}) 96 t.Log("Verify can schedule an upgrade") 97 err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Height: s.ctx.BlockHeight() + 1}}) 98 require.Nil(t, err) 99 100 VerifyDoUpgrade(t) 101 } 102 103 func TestCanOverwriteScheduleUpgrade(t *testing.T) { 104 s := setupTest(10, map[int64]bool{}) 105 t.Log("Can overwrite plan") 106 err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "bad_test", Height: s.ctx.BlockHeight() + 10}}) 107 require.Nil(t, err) 108 err = s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Height: s.ctx.BlockHeight() + 1}}) 109 require.Nil(t, err) 110 111 VerifyDoUpgrade(t) 112 } 113 114 func VerifyDoUpgrade(t *testing.T) { 115 t.Log("Verify that a panic happens at the upgrade time/height") 116 newCtx := s.ctx.WithBlockHeight(s.ctx.BlockHeight() + 1).WithBlockTime(time.Now()) 117 118 req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()} 119 require.Panics(t, func() { 120 s.module.BeginBlock(newCtx, req) 121 }) 122 123 t.Log("Verify that the upgrade can be successfully applied with a handler") 124 s.keeper.SetUpgradeHandler("test", func(ctx sdk.Context, plan upgrade.Plan) {}) 125 require.NotPanics(t, func() { 126 s.module.BeginBlock(newCtx, req) 127 }) 128 129 VerifyCleared(t, newCtx) 130 } 131 132 func VerifyDoUpgradeWithCtx(t *testing.T, newCtx sdk.Context, proposalName string) { 133 t.Log("Verify that a panic happens at the upgrade time/height") 134 req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()} 135 require.Panics(t, func() { 136 s.module.BeginBlock(newCtx, req) 137 }) 138 139 t.Log("Verify that the upgrade can be successfully applied with a handler") 140 s.keeper.SetUpgradeHandler(proposalName, func(ctx sdk.Context, plan upgrade.Plan) {}) 141 require.NotPanics(t, func() { 142 s.module.BeginBlock(newCtx, req) 143 }) 144 145 VerifyCleared(t, newCtx) 146 } 147 148 func TestHaltIfTooNew(t *testing.T) { 149 s := setupTest(10, map[int64]bool{}) 150 t.Log("Verify that we don't panic with registered plan not in database at all") 151 var called int 152 s.keeper.SetUpgradeHandler("future", func(ctx sdk.Context, plan upgrade.Plan) { called++ }) 153 154 newCtx := s.ctx.WithBlockHeight(s.ctx.BlockHeight() + 1).WithBlockTime(time.Now()) 155 req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()} 156 require.NotPanics(t, func() { 157 s.module.BeginBlock(newCtx, req) 158 }) 159 require.Equal(t, 0, called) 160 161 t.Log("Verify we panic if we have a registered handler ahead of time") 162 err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "future", Height: s.ctx.BlockHeight() + 3}}) 163 require.NoError(t, err) 164 require.Panics(t, func() { 165 s.module.BeginBlock(newCtx, req) 166 }) 167 require.Equal(t, 0, called) 168 169 t.Log("Verify we no longer panic if the plan is on time") 170 171 futCtx := s.ctx.WithBlockHeight(s.ctx.BlockHeight() + 3).WithBlockTime(time.Now()) 172 req = abci.RequestBeginBlock{Header: futCtx.BlockHeader()} 173 require.NotPanics(t, func() { 174 s.module.BeginBlock(futCtx, req) 175 }) 176 require.Equal(t, 1, called) 177 178 VerifyCleared(t, futCtx) 179 } 180 181 func VerifyCleared(t *testing.T, newCtx sdk.Context) { 182 t.Log("Verify that the upgrade plan has been cleared") 183 bz, err := s.querier(newCtx, []string{upgrade.QueryCurrent}, abci.RequestQuery{}) 184 require.NoError(t, err) 185 require.Nil(t, bz) 186 } 187 188 func TestCanClear(t *testing.T) { 189 s := setupTest(10, map[int64]bool{}) 190 t.Log("Verify upgrade is scheduled") 191 err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Time: time.Now()}}) 192 require.Nil(t, err) 193 194 err = s.handler(s.ctx, upgrade.CancelSoftwareUpgradeProposal{Title: "cancel"}) 195 require.Nil(t, err) 196 197 VerifyCleared(t, s.ctx) 198 } 199 200 func TestCantApplySameUpgradeTwice(t *testing.T) { 201 s := setupTest(10, map[int64]bool{}) 202 err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Time: time.Now()}}) 203 require.Nil(t, err) 204 VerifyDoUpgrade(t) 205 t.Log("Verify an executed upgrade \"test\" can't be rescheduled") 206 err = s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Time: time.Now()}}) 207 require.NotNil(t, err) 208 require.True(t, errors.Is(sdkerrors.ErrInvalidRequest, err), err) 209 } 210 211 func TestNoSpuriousUpgrades(t *testing.T) { 212 s := setupTest(10, map[int64]bool{}) 213 t.Log("Verify that no upgrade panic is triggered in the BeginBlocker when we haven't scheduled an upgrade") 214 req := abci.RequestBeginBlock{Header: s.ctx.BlockHeader()} 215 require.NotPanics(t, func() { 216 s.module.BeginBlock(s.ctx, req) 217 }) 218 } 219 220 func TestPlanStringer(t *testing.T) { 221 ti, err := time.Parse(time.RFC3339, "2020-01-01T00:00:00Z") 222 require.Nil(t, err) 223 require.Equal(t, `Upgrade Plan 224 Name: test 225 Time: 2020-01-01T00:00:00Z 226 Info: `, upgrade.Plan{Name: "test", Time: ti}.String()) 227 require.Equal(t, `Upgrade Plan 228 Name: test 229 Height: 100 230 Info: `, upgrade.Plan{Name: "test", Height: 100}.String()) 231 } 232 233 func VerifyNotDone(t *testing.T, newCtx sdk.Context, name string) { 234 t.Log("Verify that upgrade was not done") 235 height := s.keeper.GetDoneHeight(newCtx, name) 236 require.Zero(t, height) 237 } 238 239 func VerifyDone(t *testing.T, newCtx sdk.Context, name string) { 240 t.Log("Verify that the upgrade plan has been executed") 241 height := s.keeper.GetDoneHeight(newCtx, name) 242 require.NotZero(t, height) 243 } 244 245 func VerifySet(t *testing.T, skipUpgradeHeights map[int64]bool) { 246 t.Log("Verify if the skip upgrade has been set") 247 248 for k := range skipUpgradeHeights { 249 require.True(t, s.keeper.IsSkipHeight(k)) 250 } 251 } 252 253 func TestContains(t *testing.T) { 254 var ( 255 skipOne int64 = 11 256 ) 257 s := setupTest(10, map[int64]bool{skipOne: true}) 258 259 VerifySet(t, map[int64]bool{skipOne: true}) 260 t.Log("case where array contains the element") 261 require.True(t, s.keeper.IsSkipHeight(11)) 262 263 t.Log("case where array doesn't contain the element") 264 require.False(t, s.keeper.IsSkipHeight(4)) 265 } 266 267 func TestSkipUpgradeSkippingAll(t *testing.T) { 268 var ( 269 skipOne int64 = 11 270 skipTwo int64 = 20 271 ) 272 s := setupTest(10, map[int64]bool{skipOne: true, skipTwo: true}) 273 274 newCtx := s.ctx 275 276 req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()} 277 err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Height: skipOne}}) 278 require.NoError(t, err) 279 280 t.Log("Verify if skip upgrade flag clears upgrade plan in both cases") 281 VerifySet(t, map[int64]bool{skipOne: true, skipTwo: true}) 282 283 newCtx.SetBlockHeight(skipOne) 284 require.NotPanics(t, func() { 285 s.module.BeginBlock(newCtx, req) 286 }) 287 288 t.Log("Verify a second proposal also is being cleared") 289 err = s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop2", Plan: upgrade.Plan{Name: "test2", Height: skipTwo}}) 290 require.NoError(t, err) 291 292 newCtx.SetBlockHeight(skipTwo) 293 require.NotPanics(t, func() { 294 s.module.BeginBlock(newCtx, req) 295 }) 296 297 // To ensure verification is being done only after both upgrades are cleared 298 t.Log("Verify if both proposals are cleared") 299 VerifyCleared(t, s.ctx) 300 VerifyNotDone(t, s.ctx, "test") 301 VerifyNotDone(t, s.ctx, "test2") 302 } 303 304 func TestUpgradeSkippingOne(t *testing.T) { 305 var ( 306 skipOne int64 = 11 307 skipTwo int64 = 20 308 ) 309 s := setupTest(10, map[int64]bool{skipOne: true}) 310 311 newCtx := s.ctx 312 313 req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()} 314 err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Height: skipOne}}) 315 require.Nil(t, err) 316 317 t.Log("Verify if skip upgrade flag clears upgrade plan in one case and does upgrade on another") 318 VerifySet(t, map[int64]bool{skipOne: true}) 319 320 // Setting block height of proposal test 321 newCtx.SetBlockHeight(skipOne) 322 require.NotPanics(t, func() { 323 s.module.BeginBlock(newCtx, req) 324 }) 325 326 t.Log("Verify the second proposal is not skipped") 327 err = s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop2", Plan: upgrade.Plan{Name: "test2", Height: skipTwo}}) 328 require.Nil(t, err) 329 // Setting block height of proposal test2 330 newCtx.SetBlockHeight(skipTwo) 331 VerifyDoUpgradeWithCtx(t, newCtx, "test2") 332 333 t.Log("Verify first proposal is cleared and second is done") 334 VerifyNotDone(t, s.ctx, "test") 335 VerifyDone(t, s.ctx, "test2") 336 } 337 338 func TestUpgradeSkippingOnlyTwo(t *testing.T) { 339 var ( 340 skipOne int64 = 11 341 skipTwo int64 = 20 342 skipThree int64 = 25 343 ) 344 s := setupTest(10, map[int64]bool{skipOne: true, skipTwo: true}) 345 346 newCtx := s.ctx 347 348 req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()} 349 err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Height: skipOne}}) 350 require.Nil(t, err) 351 352 t.Log("Verify if skip upgrade flag clears upgrade plan in both cases and does third upgrade") 353 VerifySet(t, map[int64]bool{skipOne: true, skipTwo: true}) 354 355 // Setting block height of proposal test 356 newCtx.SetBlockHeight(skipOne) 357 require.NotPanics(t, func() { 358 s.module.BeginBlock(newCtx, req) 359 }) 360 361 // A new proposal with height in skipUpgradeHeights 362 err = s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop2", Plan: upgrade.Plan{Name: "test2", Height: skipTwo}}) 363 require.Nil(t, err) 364 // Setting block height of proposal test2 365 newCtx.SetBlockHeight(skipTwo) 366 require.NotPanics(t, func() { 367 s.module.BeginBlock(newCtx, req) 368 }) 369 370 t.Log("Verify a new proposal is not skipped") 371 err = s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop3", Plan: upgrade.Plan{Name: "test3", Height: skipThree}}) 372 require.Nil(t, err) 373 newCtx.SetBlockHeight(skipThree) 374 VerifyDoUpgradeWithCtx(t, newCtx, "test3") 375 376 t.Log("Verify two proposals are cleared and third is done") 377 VerifyNotDone(t, s.ctx, "test") 378 VerifyNotDone(t, s.ctx, "test2") 379 VerifyDone(t, s.ctx, "test3") 380 } 381 382 func TestUpgradeWithoutSkip(t *testing.T) { 383 s := setupTest(10, map[int64]bool{}) 384 newCtx := s.ctx.WithBlockHeight(s.ctx.BlockHeight() + 1).WithBlockTime(time.Now()) 385 req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()} 386 err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Height: s.ctx.BlockHeight() + 1}}) 387 require.Nil(t, err) 388 t.Log("Verify if upgrade happens without skip upgrade") 389 require.Panics(t, func() { 390 s.module.BeginBlock(newCtx, req) 391 }) 392 393 VerifyDoUpgrade(t) 394 VerifyDone(t, s.ctx, "test") 395 }