-
Notifications
You must be signed in to change notification settings - Fork 5
/
slottime.go
260 lines (228 loc) · 9.12 KB
/
slottime.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
package slots
import (
"fmt"
"math"
"time"
"github.com/pkg/errors"
"github.com/theQRL/qrysm/v4/config/params"
"github.com/theQRL/qrysm/v4/consensus-types/primitives"
mathutil "github.com/theQRL/qrysm/v4/math"
prysmTime "github.com/theQRL/qrysm/v4/time"
)
// MaxSlotBuffer specifies the max buffer given to slots from
// incoming objects. (24 mins with mainnet spec)
const MaxSlotBuffer = uint64(1 << 7)
// startFromTime returns the slot start in terms of genesis time.Time
func startFromTime(genesis time.Time, slot primitives.Slot) time.Time {
duration := time.Second * time.Duration(slot.Mul(params.BeaconConfig().SecondsPerSlot))
return genesis.Add(duration) // lint:ignore uintcast -- Genesis timestamp will not exceed int64 in your lifetime.
}
// StartTime returns the start time in terms of its unix epoch
// value.
func StartTime(genesis uint64, slot primitives.Slot) time.Time {
genesisTime := time.Unix(int64(genesis), 0) // lint:ignore uintcast -- Genesis timestamp will not exceed int64 in your lifetime.
return startFromTime(genesisTime, slot)
}
// SinceGenesis returns the number of slots since
// the provided genesis time.
func SinceGenesis(genesis time.Time) primitives.Slot {
if genesis.After(prysmTime.Now()) { // Genesis has not occurred yet.
return 0
}
return primitives.Slot(uint64(prysmTime.Since(genesis).Seconds()) / params.BeaconConfig().SecondsPerSlot)
}
// EpochsSinceGenesis returns the number of epochs since
// the provided genesis time.
func EpochsSinceGenesis(genesis time.Time) primitives.Epoch {
return primitives.Epoch(SinceGenesis(genesis) / params.BeaconConfig().SlotsPerEpoch)
}
// DivideSlotBy divides the SECONDS_PER_SLOT configuration
// parameter by a specified number. It returns a value of time.Duration
// in milliseconds, useful for dividing values such as 1 second into
// millisecond-based durations.
func DivideSlotBy(timesPerSlot int64) time.Duration {
return time.Duration(int64(params.BeaconConfig().SecondsPerSlot*1000)/timesPerSlot) * time.Millisecond
}
// MultiplySlotBy multiplies the SECONDS_PER_SLOT configuration
// parameter by a specified number. It returns a value of time.Duration
// in millisecond-based durations.
func MultiplySlotBy(times int64) time.Duration {
return time.Duration(int64(params.BeaconConfig().SecondsPerSlot)*times) * time.Second
}
// AbsoluteValueSlotDifference between two slots.
func AbsoluteValueSlotDifference(x, y primitives.Slot) uint64 {
if x > y {
return uint64(x.SubSlot(y))
}
return uint64(y.SubSlot(x))
}
// ToEpoch returns the epoch number of the input slot.
//
// Spec pseudocode definition:
//
// def compute_epoch_at_slot(slot: Slot) -> Epoch:
// """
// Return the epoch number at ``slot``.
// """
// return Epoch(slot // SLOTS_PER_EPOCH)
func ToEpoch(slot primitives.Slot) primitives.Epoch {
return primitives.Epoch(slot.DivSlot(params.BeaconConfig().SlotsPerEpoch))
}
// EpochStart returns the first slot number of the
// current epoch.
//
// Spec pseudocode definition:
//
// def compute_start_slot_at_epoch(epoch: Epoch) -> Slot:
// """
// Return the start slot of ``epoch``.
// """
// return Slot(epoch * SLOTS_PER_EPOCH)
func EpochStart(epoch primitives.Epoch) (primitives.Slot, error) {
slot, err := params.BeaconConfig().SlotsPerEpoch.SafeMul(uint64(epoch))
if err != nil {
return slot, errors.Errorf("start slot calculation overflows: %v", err)
}
return slot, nil
}
// EpochEnd returns the last slot number of the
// current epoch.
func EpochEnd(epoch primitives.Epoch) (primitives.Slot, error) {
if epoch == math.MaxUint64 {
return 0, errors.New("start slot calculation overflows")
}
slot, err := EpochStart(epoch + 1)
if err != nil {
return 0, err
}
return slot - 1, nil
}
// IsEpochStart returns true if the given slot number is an epoch starting slot
// number.
func IsEpochStart(slot primitives.Slot) bool {
return slot%params.BeaconConfig().SlotsPerEpoch == 0
}
// IsEpochEnd returns true if the given slot number is an epoch ending slot
// number.
func IsEpochEnd(slot primitives.Slot) bool {
return IsEpochStart(slot + 1)
}
// SinceEpochStarts returns number of slots since the start of the epoch.
func SinceEpochStarts(slot primitives.Slot) primitives.Slot {
return slot % params.BeaconConfig().SlotsPerEpoch
}
// VerifyTime validates the input slot is not from the future.
func VerifyTime(genesisTime uint64, slot primitives.Slot, timeTolerance time.Duration) error {
slotTime, err := ToTime(genesisTime, slot)
if err != nil {
return err
}
// Defensive check to ensure unreasonable slots are rejected
// straight away.
if err := ValidateClock(slot, genesisTime); err != nil {
return err
}
currentTime := prysmTime.Now()
diff := slotTime.Sub(currentTime)
if diff > timeTolerance {
return fmt.Errorf("could not process slot from the future, slot time %s > current time %s", slotTime, currentTime)
}
return nil
}
// ToTime takes the given slot and genesis time to determine the start time of the slot.
func ToTime(genesisTimeSec uint64, slot primitives.Slot) (time.Time, error) {
timeSinceGenesis, err := slot.SafeMul(params.BeaconConfig().SecondsPerSlot)
if err != nil {
return time.Unix(0, 0), fmt.Errorf("slot (%d) is in the far distant future: %w", slot, err)
}
sTime, err := timeSinceGenesis.SafeAdd(genesisTimeSec)
if err != nil {
return time.Unix(0, 0), fmt.Errorf("slot (%d) is in the far distant future: %w", slot, err)
}
return time.Unix(int64(sTime), 0), nil // lint:ignore uintcast -- A timestamp will not exceed int64 in your lifetime.
}
// Since computes the number of time slots that have occurred since the given timestamp.
func Since(time time.Time) primitives.Slot {
return CurrentSlot(uint64(time.Unix()))
}
// CurrentSlot returns the current slot as determined by the local clock and
// provided genesis time.
func CurrentSlot(genesisTimeSec uint64) primitives.Slot {
now := uint64(prysmTime.Now().Unix())
if now < genesisTimeSec {
return 0
}
return primitives.Slot((now - genesisTimeSec) / params.BeaconConfig().SecondsPerSlot)
}
// Duration computes the span of time between two instants, represented as Slots.
func Duration(start, end time.Time) primitives.Slot {
if end.Before(start) {
return 0
}
return primitives.Slot(uint64(end.Unix()-start.Unix()) / params.BeaconConfig().SecondsPerSlot)
}
// ValidateClock validates a provided slot against the local
// clock to ensure slots that are unreasonable are returned with
// an error.
func ValidateClock(slot primitives.Slot, genesisTimeSec uint64) error {
maxPossibleSlot := CurrentSlot(genesisTimeSec).Add(MaxSlotBuffer)
// Defensive check to ensure that we only process slots up to a hard limit
// from our local clock.
if slot > maxPossibleSlot {
return fmt.Errorf("slot %d > %d which exceeds max allowed value relative to the local clock", slot, maxPossibleSlot)
}
return nil
}
// RoundUpToNearestEpoch rounds up the provided slot value to the nearest epoch.
func RoundUpToNearestEpoch(slot primitives.Slot) primitives.Slot {
if slot%params.BeaconConfig().SlotsPerEpoch != 0 {
slot -= slot % params.BeaconConfig().SlotsPerEpoch
slot += params.BeaconConfig().SlotsPerEpoch
}
return slot
}
// VotingPeriodStartTime returns the current voting period's start time
// depending on the provided genesis and current slot.
func VotingPeriodStartTime(genesis uint64, slot primitives.Slot) uint64 {
slots := params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().EpochsPerEth1VotingPeriod))
startTime := uint64((slot - slot.ModSlot(slots)).Mul(params.BeaconConfig().SecondsPerSlot))
return genesis + startTime
}
// PrevSlot returns previous slot, with an exception in slot 0 to prevent underflow.
func PrevSlot(slot primitives.Slot) primitives.Slot {
if slot > 0 {
return slot.Sub(1)
}
return 0
}
// SyncCommitteePeriod returns the sync committee period of input epoch `e`.
//
// Spec code:
// def compute_sync_committee_period(epoch: Epoch) -> uint64:
//
// return epoch // EPOCHS_PER_SYNC_COMMITTEE_PERIOD
func SyncCommitteePeriod(e primitives.Epoch) uint64 {
return uint64(e / params.BeaconConfig().EpochsPerSyncCommitteePeriod)
}
// SyncCommitteePeriodStartEpoch returns the start epoch of a sync committee period.
func SyncCommitteePeriodStartEpoch(e primitives.Epoch) (primitives.Epoch, error) {
// Overflow is impossible here because of division of `EPOCHS_PER_SYNC_COMMITTEE_PERIOD`.
startEpoch, err := mathutil.Mul64(SyncCommitteePeriod(e), uint64(params.BeaconConfig().EpochsPerSyncCommitteePeriod))
if err != nil {
return 0, err
}
return primitives.Epoch(startEpoch), nil
}
// SecondsSinceSlotStart returns the number of seconds transcurred since the
// given slot start time
func SecondsSinceSlotStart(s primitives.Slot, genesisTime, timeStamp uint64) (uint64, error) {
if timeStamp < genesisTime+uint64(s)*params.BeaconConfig().SecondsPerSlot {
return 0, errors.New("could not compute seconds since slot start: invalid timestamp")
}
return timeStamp - genesisTime - uint64(s)*params.BeaconConfig().SecondsPerSlot, nil
}
// TimeIntoSlot returns the time duration elapsed between the current time and
// the start of the current slot
func TimeIntoSlot(genesisTime uint64) time.Duration {
return time.Since(StartTime(genesisTime, CurrentSlot(genesisTime)))
}