github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/core/helpers/slot_epoch_test.go (about)

     1  package helpers
     2  
     3  import (
     4  	"math"
     5  	"testing"
     6  	"time"
     7  
     8  	types "github.com/prysmaticlabs/eth2-types"
     9  	"github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
    10  	pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
    11  	"github.com/prysmaticlabs/prysm/shared/params"
    12  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    13  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    14  	"github.com/prysmaticlabs/prysm/shared/timeutils"
    15  )
    16  
    17  func TestSlotToEpoch_OK(t *testing.T) {
    18  	tests := []struct {
    19  		slot  types.Slot
    20  		epoch types.Epoch
    21  	}{
    22  		{slot: 0, epoch: 0},
    23  		{slot: 50, epoch: 1},
    24  		{slot: 64, epoch: 2},
    25  		{slot: 128, epoch: 4},
    26  		{slot: 200, epoch: 6},
    27  	}
    28  	for _, tt := range tests {
    29  		assert.Equal(t, tt.epoch, SlotToEpoch(tt.slot), "SlotToEpoch(%d)", tt.slot)
    30  	}
    31  }
    32  
    33  func TestCurrentEpoch_OK(t *testing.T) {
    34  	tests := []struct {
    35  		slot  types.Slot
    36  		epoch types.Epoch
    37  	}{
    38  		{slot: 0, epoch: 0},
    39  		{slot: 50, epoch: 1},
    40  		{slot: 64, epoch: 2},
    41  		{slot: 128, epoch: 4},
    42  		{slot: 200, epoch: 6},
    43  	}
    44  	for _, tt := range tests {
    45  		state, err := v1.InitializeFromProto(&pb.BeaconState{Slot: tt.slot})
    46  		require.NoError(t, err)
    47  		assert.Equal(t, tt.epoch, CurrentEpoch(state), "ActiveCurrentEpoch(%d)", state.Slot())
    48  	}
    49  }
    50  
    51  func TestPrevEpoch_OK(t *testing.T) {
    52  	tests := []struct {
    53  		slot  types.Slot
    54  		epoch types.Epoch
    55  	}{
    56  		{slot: 0, epoch: 0},
    57  		{slot: 0 + params.BeaconConfig().SlotsPerEpoch + 1, epoch: 0},
    58  		{slot: 2 * params.BeaconConfig().SlotsPerEpoch, epoch: 1},
    59  	}
    60  	for _, tt := range tests {
    61  		state, err := v1.InitializeFromProto(&pb.BeaconState{Slot: tt.slot})
    62  		require.NoError(t, err)
    63  		assert.Equal(t, tt.epoch, PrevEpoch(state), "ActivePrevEpoch(%d)", state.Slot())
    64  	}
    65  }
    66  
    67  func TestNextEpoch_OK(t *testing.T) {
    68  	tests := []struct {
    69  		slot  types.Slot
    70  		epoch types.Epoch
    71  	}{
    72  		{slot: 0, epoch: types.Epoch(0/params.BeaconConfig().SlotsPerEpoch + 1)},
    73  		{slot: 50, epoch: types.Epoch(0/params.BeaconConfig().SlotsPerEpoch + 2)},
    74  		{slot: 64, epoch: types.Epoch(64/params.BeaconConfig().SlotsPerEpoch + 1)},
    75  		{slot: 128, epoch: types.Epoch(128/params.BeaconConfig().SlotsPerEpoch + 1)},
    76  		{slot: 200, epoch: types.Epoch(200/params.BeaconConfig().SlotsPerEpoch + 1)},
    77  	}
    78  	for _, tt := range tests {
    79  		state, err := v1.InitializeFromProto(&pb.BeaconState{Slot: tt.slot})
    80  		require.NoError(t, err)
    81  		assert.Equal(t, tt.epoch, NextEpoch(state), "NextEpoch(%d)", state.Slot())
    82  	}
    83  }
    84  
    85  func TestEpochStartSlot_OK(t *testing.T) {
    86  	tests := []struct {
    87  		epoch     types.Epoch
    88  		startSlot types.Slot
    89  		error     bool
    90  	}{
    91  		{epoch: 0, startSlot: 0 * params.BeaconConfig().SlotsPerEpoch, error: false},
    92  		{epoch: 1, startSlot: 1 * params.BeaconConfig().SlotsPerEpoch, error: false},
    93  		{epoch: 10, startSlot: 10 * params.BeaconConfig().SlotsPerEpoch, error: false},
    94  		{epoch: 1 << 58, startSlot: 1 << 63, error: false},
    95  		{epoch: 1 << 59, startSlot: 1 << 63, error: true},
    96  		{epoch: 1 << 60, startSlot: 1 << 63, error: true},
    97  	}
    98  	for _, tt := range tests {
    99  		ss, err := StartSlot(tt.epoch)
   100  		if !tt.error {
   101  			require.NoError(t, err)
   102  			assert.Equal(t, tt.startSlot, ss, "StartSlot(%d)", tt.epoch)
   103  		} else {
   104  			require.ErrorContains(t, "start slot calculation overflow", err)
   105  		}
   106  	}
   107  }
   108  
   109  func TestEpochEndSlot_OK(t *testing.T) {
   110  	tests := []struct {
   111  		epoch     types.Epoch
   112  		startSlot types.Slot
   113  		error     bool
   114  	}{
   115  		{epoch: 0, startSlot: 1*params.BeaconConfig().SlotsPerEpoch - 1, error: false},
   116  		{epoch: 1, startSlot: 2*params.BeaconConfig().SlotsPerEpoch - 1, error: false},
   117  		{epoch: 10, startSlot: 11*params.BeaconConfig().SlotsPerEpoch - 1, error: false},
   118  		{epoch: 1 << 59, startSlot: 1 << 63, error: true},
   119  		{epoch: 1 << 60, startSlot: 1 << 63, error: true},
   120  		{epoch: math.MaxUint64, startSlot: 0, error: true},
   121  	}
   122  	for _, tt := range tests {
   123  		ss, err := EndSlot(tt.epoch)
   124  		if !tt.error {
   125  			require.NoError(t, err)
   126  			assert.Equal(t, tt.startSlot, ss, "StartSlot(%d)", tt.epoch)
   127  		} else {
   128  			require.ErrorContains(t, "start slot calculation overflow", err)
   129  		}
   130  	}
   131  }
   132  
   133  func TestIsEpochStart(t *testing.T) {
   134  	epochLength := params.BeaconConfig().SlotsPerEpoch
   135  
   136  	tests := []struct {
   137  		slot   types.Slot
   138  		result bool
   139  	}{
   140  		{
   141  			slot:   epochLength + 1,
   142  			result: false,
   143  		},
   144  		{
   145  			slot:   epochLength - 1,
   146  			result: false,
   147  		},
   148  		{
   149  			slot:   epochLength,
   150  			result: true,
   151  		},
   152  		{
   153  			slot:   epochLength * 2,
   154  			result: true,
   155  		},
   156  	}
   157  
   158  	for _, tt := range tests {
   159  		assert.Equal(t, tt.result, IsEpochStart(tt.slot), "IsEpochStart(%d)", tt.slot)
   160  	}
   161  }
   162  
   163  func TestIsEpochEnd(t *testing.T) {
   164  	epochLength := params.BeaconConfig().SlotsPerEpoch
   165  
   166  	tests := []struct {
   167  		slot   types.Slot
   168  		result bool
   169  	}{
   170  		{
   171  			slot:   epochLength + 1,
   172  			result: false,
   173  		},
   174  		{
   175  			slot:   epochLength,
   176  			result: false,
   177  		},
   178  		{
   179  			slot:   epochLength - 1,
   180  			result: true,
   181  		},
   182  	}
   183  
   184  	for _, tt := range tests {
   185  		assert.Equal(t, tt.result, IsEpochEnd(tt.slot), "IsEpochEnd(%d)", tt.slot)
   186  	}
   187  }
   188  
   189  func TestSlotsSinceEpochStarts(t *testing.T) {
   190  	tests := []struct {
   191  		slots       types.Slot
   192  		wantedSlots types.Slot
   193  	}{
   194  		{slots: 0, wantedSlots: 0},
   195  		{slots: 1, wantedSlots: 1},
   196  		{slots: params.BeaconConfig().SlotsPerEpoch - 1, wantedSlots: params.BeaconConfig().SlotsPerEpoch - 1},
   197  		{slots: params.BeaconConfig().SlotsPerEpoch + 1, wantedSlots: 1},
   198  		{slots: 10*params.BeaconConfig().SlotsPerEpoch + 2, wantedSlots: 2},
   199  	}
   200  	for _, tt := range tests {
   201  		assert.Equal(t, tt.wantedSlots, SlotsSinceEpochStarts(tt.slots))
   202  	}
   203  }
   204  
   205  func TestRoundUpToNearestEpoch_OK(t *testing.T) {
   206  	tests := []struct {
   207  		startSlot     types.Slot
   208  		roundedUpSlot types.Slot
   209  	}{
   210  		{startSlot: 0 * params.BeaconConfig().SlotsPerEpoch, roundedUpSlot: 0},
   211  		{startSlot: 1*params.BeaconConfig().SlotsPerEpoch - 10, roundedUpSlot: 1 * params.BeaconConfig().SlotsPerEpoch},
   212  		{startSlot: 10*params.BeaconConfig().SlotsPerEpoch - (params.BeaconConfig().SlotsPerEpoch - 1), roundedUpSlot: 10 * params.BeaconConfig().SlotsPerEpoch},
   213  	}
   214  	for _, tt := range tests {
   215  		assert.Equal(t, tt.roundedUpSlot, RoundUpToNearestEpoch(tt.startSlot), "RoundUpToNearestEpoch(%d)", tt.startSlot)
   216  	}
   217  }
   218  
   219  func TestSlotToTime(t *testing.T) {
   220  	type args struct {
   221  		genesisTimeSec uint64
   222  		slot           types.Slot
   223  	}
   224  	tests := []struct {
   225  		name      string
   226  		args      args
   227  		want      time.Time
   228  		wantedErr string
   229  	}{
   230  		{
   231  			name: "slot_0",
   232  			args: args{
   233  				genesisTimeSec: 0,
   234  				slot:           0,
   235  			},
   236  			want: time.Unix(0, 0),
   237  		},
   238  		{
   239  			name: "slot_1",
   240  			args: args{
   241  				genesisTimeSec: 0,
   242  				slot:           1,
   243  			},
   244  			want: time.Unix(int64(1*params.BeaconConfig().SecondsPerSlot), 0),
   245  		},
   246  		{
   247  			name: "slot_12",
   248  			args: args{
   249  				genesisTimeSec: 500,
   250  				slot:           12,
   251  			},
   252  			want: time.Unix(500+int64(12*params.BeaconConfig().SecondsPerSlot), 0),
   253  		},
   254  		{
   255  			name: "overflow",
   256  			args: args{
   257  				genesisTimeSec: 500,
   258  				slot:           math.MaxUint64,
   259  			},
   260  			wantedErr: "is in the far distant future",
   261  		},
   262  	}
   263  	for _, tt := range tests {
   264  		t.Run(tt.name, func(t *testing.T) {
   265  			got, err := SlotToTime(tt.args.genesisTimeSec, tt.args.slot)
   266  			if tt.wantedErr != "" {
   267  				assert.ErrorContains(t, tt.wantedErr, err)
   268  			} else {
   269  				assert.NoError(t, err)
   270  				assert.DeepEqual(t, tt.want, got)
   271  			}
   272  		})
   273  	}
   274  }
   275  
   276  func TestVerifySlotTime(t *testing.T) {
   277  	type args struct {
   278  		genesisTime   int64
   279  		slot          types.Slot
   280  		timeTolerance time.Duration
   281  	}
   282  	tests := []struct {
   283  		name      string
   284  		args      args
   285  		wantedErr string
   286  	}{
   287  		{
   288  			name: "Past slot",
   289  			args: args{
   290  				genesisTime: timeutils.Now().Add(-1 * 5 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second).Unix(),
   291  				slot:        3,
   292  			},
   293  		},
   294  		{
   295  			name: "within tolerance",
   296  			args: args{
   297  				genesisTime: timeutils.Now().Add(-1 * 5 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second).Add(20 * time.Millisecond).Unix(),
   298  				slot:        5,
   299  			},
   300  		},
   301  		{
   302  			name: "future slot",
   303  			args: args{
   304  				genesisTime: timeutils.Now().Add(-1 * 5 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second).Unix(),
   305  				slot:        6,
   306  			},
   307  			wantedErr: "could not process slot from the future",
   308  		},
   309  		{
   310  			name: "max future slot",
   311  			args: args{
   312  				genesisTime: timeutils.Now().Add(-1 * 5 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second).Unix(),
   313  				slot:        types.Slot(MaxSlotBuffer + 6),
   314  			},
   315  			wantedErr: "exceeds max allowed value relative to the local clock",
   316  		},
   317  		{
   318  			name: "evil future slot",
   319  			args: args{
   320  				genesisTime: timeutils.Now().Add(-1 * 24 * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second).Unix(), // 24 slots in the past
   321  				// Gets multiplied with slot duration, and results in an overflow. Wraps around to a valid time.
   322  				// Lower than max signed int. And chosen specifically to wrap to a valid slot 24
   323  				slot: types.Slot((^uint64(0))/params.BeaconConfig().SecondsPerSlot) + 24,
   324  			},
   325  			wantedErr: "is in the far distant future",
   326  		},
   327  	}
   328  	for _, tt := range tests {
   329  		t.Run(tt.name, func(t *testing.T) {
   330  			err := VerifySlotTime(uint64(tt.args.genesisTime), tt.args.slot, tt.args.timeTolerance)
   331  			if tt.wantedErr != "" {
   332  				assert.ErrorContains(t, tt.wantedErr, err)
   333  			} else {
   334  				assert.NoError(t, err)
   335  			}
   336  		})
   337  	}
   338  }
   339  
   340  func TestValidateSlotClock_HandlesBadSlot(t *testing.T) {
   341  	genTime := timeutils.Now().Add(-1 * time.Duration(MaxSlotBuffer) * time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second).Unix()
   342  
   343  	assert.NoError(t, ValidateSlotClock(types.Slot(MaxSlotBuffer), uint64(genTime)), "unexpected error validating slot")
   344  	assert.NoError(t, ValidateSlotClock(types.Slot(2*MaxSlotBuffer), uint64(genTime)), "unexpected error validating slot")
   345  	assert.ErrorContains(t, "which exceeds max allowed value relative to the local clock", ValidateSlotClock(types.Slot(2*MaxSlotBuffer+1), uint64(genTime)), "no error from bad slot")
   346  	assert.ErrorContains(t, "which exceeds max allowed value relative to the local clock", ValidateSlotClock(1<<63, uint64(genTime)), "no error from bad slot")
   347  }
   348  
   349  func TestPrevSlot(t *testing.T) {
   350  	tests := []struct {
   351  		name string
   352  		slot types.Slot
   353  		want types.Slot
   354  	}{
   355  		{
   356  			name: "no underflow",
   357  			slot: 0,
   358  			want: 0,
   359  		},
   360  		{
   361  			name: "slot 1",
   362  			slot: 1,
   363  			want: 0,
   364  		},
   365  		{
   366  			name: "slot 2",
   367  			slot: 2,
   368  			want: 1,
   369  		},
   370  		{
   371  			name: "max",
   372  			slot: 1<<64 - 1,
   373  			want: 1<<64 - 1 - 1,
   374  		},
   375  	}
   376  	for _, tt := range tests {
   377  		t.Run(tt.name, func(t *testing.T) {
   378  			if got := PrevSlot(tt.slot); got != tt.want {
   379  				t.Errorf("PrevSlot() = %v, want %v", got, tt.want)
   380  			}
   381  		})
   382  	}
   383  }