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  }