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  }