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 }