github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/rolldpos/epoch_test.go (about)

     1  package rolldpos
     2  
     3  import (
     4  	"context"
     5  	"strconv"
     6  	"testing"
     7  
     8  	"github.com/golang/mock/gomock"
     9  	"github.com/pkg/errors"
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/iotexproject/iotex-core/test/mock/mock_chainmanager"
    13  )
    14  
    15  func TestEnableDardanellesSubEpoch(t *testing.T) {
    16  	require := require.New(t)
    17  	numSubEpochs := 1
    18  	options := EnableDardanellesSubEpoch(uint64(0), uint64(numSubEpochs))
    19  	p := NewProtocol(23, 4, 3)
    20  	require.Nil(options(p))
    21  	require.NotNil(options)
    22  }
    23  
    24  func TestNewProtocol(t *testing.T) {
    25  	require := require.New(t)
    26  	numCandidateDelegates := uint64(23)
    27  	numDelegates := uint64(24)
    28  	numSubEpochs := uint64(3)
    29  	height := 0
    30  	options := EnableDardanellesSubEpoch(uint64(height), numSubEpochs)
    31  	require.NotNil(NewProtocol(numCandidateDelegates, numDelegates, numSubEpochs, options))
    32  }
    33  
    34  func TestProtocol_Handle(t *testing.T) {
    35  	require := require.New(t)
    36  	p := NewProtocol(23, 4, 3)
    37  	ctx := context.Background()
    38  	receipt, error := p.Handle(ctx, nil, nil)
    39  	require.Nil(receipt)
    40  	require.NoError(error)
    41  }
    42  
    43  func TestProtocol_NumCandidateDelegates(t *testing.T) {
    44  	require := require.New(t)
    45  	p := NewProtocol(23, 4, 3)
    46  	require.Equal(uint64(23), p.NumCandidateDelegates())
    47  }
    48  
    49  func TestProtocol_NumDelegates(t *testing.T) {
    50  	require := require.New(t)
    51  	p := NewProtocol(23, 4, 3)
    52  	require.Equal(uint64(4), p.NumDelegates())
    53  }
    54  
    55  func TestProtocol_ReadState(t *testing.T) {
    56  	require := require.New(t)
    57  	p := NewProtocol(23, 4, 3)
    58  	ctx := context.Background()
    59  	methods := [8]string{
    60  		"NumCandidateDelegates",
    61  		"NumDelegates",
    62  		"NumSubEpochs",
    63  		"EpochNumber",
    64  		"EpochHeight",
    65  		"EpochLastHeight",
    66  		"SubEpochNumber",
    67  		"trick",
    68  	}
    69  
    70  	arg1 := []byte("10")
    71  	arg2 := []byte("20")
    72  
    73  	ctrl := gomock.NewController(t)
    74  	sm := mock_chainmanager.NewMockStateManager(ctrl)
    75  	sm.EXPECT().Height().Return(uint64(1), nil).AnyTimes()
    76  
    77  	arg1Num, err := strconv.ParseUint(string(arg1), 10, 64)
    78  	require.NoError(err)
    79  
    80  	for i, method := range methods {
    81  
    82  		if i != 0 && i != 1 {
    83  			result, _, err := p.ReadState(ctx, sm, []byte(method), arg1, arg2)
    84  			require.Nil(result)
    85  			require.Error(err)
    86  		}
    87  
    88  		switch method {
    89  
    90  		case "NumCandidateDelegates":
    91  			result, _, err := p.ReadState(ctx, sm, []byte(method), arg1)
    92  			require.Equal(strconv.FormatUint(p.numCandidateDelegates, 10), string(result))
    93  			require.NoError(err)
    94  
    95  		case "NumDelegates":
    96  			result, _, err := p.ReadState(ctx, sm, []byte(method), arg1)
    97  			require.Equal(strconv.FormatUint(p.numDelegates, 10), string(result))
    98  			require.NoError(err)
    99  
   100  		case "NumSubEpochs":
   101  			result, _, err := p.ReadState(ctx, sm, []byte(method), arg1)
   102  			require.Equal(strconv.FormatUint(p.NumSubEpochs(arg1Num), 10), string(result))
   103  			require.NoError(err)
   104  
   105  		case "EpochNumber":
   106  
   107  			result, _, err := p.ReadState(ctx, sm, []byte(method), arg1)
   108  			require.Equal(strconv.FormatUint(p.GetEpochNum(arg1Num), 10), string(result))
   109  			require.NoError(err)
   110  
   111  		case "EpochHeight":
   112  
   113  			result, _, err := p.ReadState(ctx, sm, []byte(method), arg1)
   114  			require.Equal(strconv.FormatUint(p.GetEpochHeight(arg1Num), 10), string(result))
   115  			require.NoError(err)
   116  
   117  		case "EpochLastHeight":
   118  
   119  			result, _, err := p.ReadState(ctx, sm, []byte(method), arg1)
   120  			require.Equal(strconv.FormatUint(p.GetEpochLastBlockHeight(arg1Num), 10), string(result))
   121  			require.NoError(err)
   122  
   123  		case "SubEpochNumber":
   124  
   125  			result, _, err := p.ReadState(ctx, sm, []byte(method), arg1)
   126  			require.Equal(strconv.FormatUint(p.GetSubEpochNum(arg1Num), 10), string(result))
   127  			require.NoError(err)
   128  
   129  		default:
   130  			result, _, err := p.ReadState(ctx, sm, []byte(method), arg1)
   131  			require.Nil(result)
   132  			require.Error(err)
   133  
   134  		}
   135  
   136  	}
   137  
   138  }
   139  
   140  func TestProtocol_NumSubEpochs(t *testing.T) {
   141  
   142  	require := require.New(t)
   143  
   144  	height := []uint64{0, 1, 12, 25, 38, 53, 59, 80, 90, 93, 120}
   145  
   146  	expectedP := []uint64{3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}
   147  
   148  	for i := 0; i < len(height); i++ {
   149  		p1 := NewProtocol(23, 4, 3)
   150  		p2 := NewProtocol(23, 4, 3)
   151  		p2.numSubEpochsDardanelles = p2.numSubEpochs
   152  		p2.dardanellesHeight = height[i]
   153  		p2.dardanellesOn = true
   154  		numSubEpochs := p1.NumSubEpochs(height[i])
   155  		require.Equal(expectedP[i], numSubEpochs)
   156  		numSubEpochs = p2.NumSubEpochs(height[i])
   157  		require.Equal(expectedP[i], numSubEpochs)
   158  	}
   159  
   160  }
   161  
   162  func TestGetEpochNum(t *testing.T) {
   163  	require := require.New(t)
   164  
   165  	height := []uint64{0, 1, 12, 25, 38, 53, 59, 80, 90, 93, 120}
   166  
   167  	expectedP1 := []uint64{0, 1, 1, 3, 4, 5, 5, 7, 8, 8, 10}
   168  	expectedP2 := []uint64{0, 1, 1, 3, 4, 5, 5, 7, 8, 8, 10}
   169  	expectedP3 := []uint64{0, 0, 1, 2, 3, 4, 4, 6, 7, 7, 10}
   170  
   171  	//If only the modification of function EnableDardanellesSubEpoch to Protocol,
   172  	//then function GetEpochNum won't jump out of the second if block
   173  	//If dardanellesOn =true, and height <= p3.dardanellesHeight,
   174  	//then p3.numSubEpochsDardanelles can't be 0.
   175  	//Assume that in addition of function EnableDardanellesSubEpoch,
   176  	//there are other function that can assign values to numSubEpochsDardanelles
   177  	for i := 0; i < len(height); i++ {
   178  		p1 := NewProtocol(23, 4, 3)
   179  
   180  		p2 := NewProtocol(23, 4, 3)
   181  		p2.numSubEpochsDardanelles = p2.numSubEpochs
   182  		p2.dardanellesHeight = height[i]
   183  		p2.dardanellesOn = true
   184  
   185  		p3 := NewProtocol(23, 4, 3)
   186  		p3.dardanellesOn = true
   187  		p3.numSubEpochsDardanelles = 3
   188  
   189  		epochNum := p1.GetEpochNum(height[i])
   190  		require.Equal(expectedP1[i], epochNum)
   191  
   192  		epochNum = p2.GetEpochNum(height[i])
   193  		require.Equal(expectedP2[i], epochNum)
   194  
   195  		epochNum = p3.GetEpochNum(height[i])
   196  		require.Equal(expectedP3[i], epochNum)
   197  	}
   198  
   199  }
   200  
   201  func TestGetEpochHeight(t *testing.T) {
   202  
   203  	require := require.New(t)
   204  	epochNum := []uint64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
   205  
   206  	expectedP1 := []uint64{0, 1, 13, 25, 37, 49, 61, 73, 85, 97, 109}
   207  	expectedP2 := []uint64{0, 12, 24, 36, 48, 60, 72, 84, 96, 108, 120}
   208  
   209  	for i := 0; i < len(epochNum); i++ {
   210  
   211  		p1 := NewProtocol(23, 4, 3)
   212  
   213  		p2 := NewProtocol(23, 4, 3)
   214  		p2.dardanellesOn = true
   215  		p2.numSubEpochsDardanelles = p2.numSubEpochs
   216  		p2.dardanellesHeight = 0 //Consider tha p2 doesn't meet the condition fo the second if block, ie height = 0
   217  
   218  		epochHeight := p1.GetEpochHeight(epochNum[i])
   219  		require.Equal(expectedP1[i], epochHeight)
   220  
   221  		epochHeight = p2.GetEpochHeight(epochNum[i])
   222  		require.Equal(expectedP2[i], epochHeight)
   223  	}
   224  
   225  }
   226  
   227  func TestGetEpochLastBlockHeight(t *testing.T) {
   228  	require := require.New(t)
   229  	p := NewProtocol(23, 4, 3)
   230  
   231  	epochNums := []uint64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
   232  	expectedHeights := []uint64{0, 12, 24, 36, 48, 60, 72, 84, 96, 108, 120}
   233  	for i, epochNum := range epochNums {
   234  		height := p.GetEpochLastBlockHeight(epochNum)
   235  		require.Equal(expectedHeights[i], height)
   236  	}
   237  }
   238  
   239  func TestGetSubEpochNum(t *testing.T) {
   240  	require := require.New(t)
   241  	p := NewProtocol(23, 4, 3)
   242  	epochHeights := []uint64{0, 1, 12, 25, 38, 53, 59, 80, 90, 93, 120}
   243  	expectedSubEpochNums := []uint64{0, 0, 2, 0, 0, 1, 2, 1, 1, 2, 2}
   244  	for i, epochHeight := range epochHeights {
   245  		subEpochNum := p.GetSubEpochNum(epochHeight)
   246  		require.Equal(expectedSubEpochNums[i], subEpochNum)
   247  	}
   248  }
   249  
   250  func productivity(epochStartHeight uint64, epochEndHeight uint64) (map[string]uint64, error) {
   251  	if epochStartHeight == 0 || epochEndHeight == 0 {
   252  		return nil, errors.New("productivity error")
   253  	}
   254  	return map[string]uint64{"ret": 0}, nil
   255  }
   256  
   257  func TestProductivityByEpoch(t *testing.T) {
   258  	require := require.New(t)
   259  	p := NewProtocol(23, 4, 3)
   260  
   261  	t.Run("normal call", func(t *testing.T) {
   262  		epochNum := uint64(1)
   263  		tipHeight := uint64(1)
   264  		expectedHeights := uint64(1)
   265  		expectedProduces := map[string]uint64{"ret": 0}
   266  		retHeight, retProduce, retError := p.ProductivityByEpoch(epochNum, tipHeight, productivity)
   267  		require.Equal(retHeight, expectedHeights)
   268  		require.Equal(retProduce, expectedProduces)
   269  		require.NoError(retError)
   270  	})
   271  
   272  	t.Run("tipHeight param error", func(t *testing.T) {
   273  		epochNum := uint64(0)
   274  		tipHeight := uint64(0)
   275  		expectedHeights := uint64(0)
   276  		expectedProduces := map[string]uint64{}
   277  		retHeight, retProduce, retError := p.ProductivityByEpoch(epochNum, tipHeight, productivity)
   278  		require.Equal(retHeight, expectedHeights)
   279  		require.Equal(retProduce, expectedProduces)
   280  		require.NoError(retError)
   281  	})
   282  
   283  	t.Run("epochNum param error", func(t *testing.T) {
   284  		epochNum := uint64(2)
   285  		tipHeight := uint64(12)
   286  		expectedHeights := uint64(0)
   287  		var expectedProduces = map[string]uint64{}
   288  		expectedProduces = nil
   289  		expectedErrors := errors.New("epoch number 2 is larger than current epoch number 1")
   290  		retHeight, retProduce, retError := p.ProductivityByEpoch(epochNum, tipHeight, productivity)
   291  		require.Equal(retHeight, expectedHeights)
   292  		require.Equal(retProduce, expectedProduces)
   293  		require.EqualError(retError, expectedErrors.Error())
   294  	})
   295  
   296  	t.Run("productivity param error", func(t *testing.T) {
   297  		epochNum := uint64(0)
   298  		tipHeight := uint64(1)
   299  		expectedHeights := uint64(1)
   300  		var expectedProduces = map[string]uint64{}
   301  		expectedProduces = nil
   302  		expectedErrors := errors.New("productivity error")
   303  		retHeight, retProduce, retError := p.ProductivityByEpoch(epochNum, tipHeight, productivity)
   304  		require.Equal(retHeight, expectedHeights)
   305  		require.Equal(retProduce, expectedProduces)
   306  		require.EqualError(retError, expectedErrors.Error())
   307  	})
   308  }