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

     1  package helpers
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"time"
     7  
     8  	"github.com/pkg/errors"
     9  	types "github.com/prysmaticlabs/eth2-types"
    10  	iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface"
    11  	"github.com/prysmaticlabs/prysm/shared/params"
    12  	"github.com/prysmaticlabs/prysm/shared/timeutils"
    13  )
    14  
    15  // MaxSlotBuffer specifies the max buffer given to slots from
    16  // incoming objects. (24 mins with mainnet spec)
    17  const MaxSlotBuffer = uint64(1 << 7)
    18  
    19  // SlotToEpoch returns the epoch number of the input slot.
    20  //
    21  // Spec pseudocode definition:
    22  //  def compute_epoch_at_slot(slot: Slot) -> Epoch:
    23  //    """
    24  //    Return the epoch number at ``slot``.
    25  //    """
    26  //    return Epoch(slot // SLOTS_PER_EPOCH)
    27  func SlotToEpoch(slot types.Slot) types.Epoch {
    28  	return types.Epoch(slot.DivSlot(params.BeaconConfig().SlotsPerEpoch))
    29  }
    30  
    31  // CurrentEpoch returns the current epoch number calculated from
    32  // the slot number stored in beacon state.
    33  //
    34  // Spec pseudocode definition:
    35  //  def get_current_epoch(state: BeaconState) -> Epoch:
    36  //    """
    37  //    Return the current epoch.
    38  //    """
    39  //    return compute_epoch_at_slot(state.slot)
    40  func CurrentEpoch(state iface.ReadOnlyBeaconState) types.Epoch {
    41  	return SlotToEpoch(state.Slot())
    42  }
    43  
    44  // PrevEpoch returns the previous epoch number calculated from
    45  // the slot number stored in beacon state. It also checks for
    46  // underflow condition.
    47  //
    48  // Spec pseudocode definition:
    49  //  def get_previous_epoch(state: BeaconState) -> Epoch:
    50  //    """`
    51  //    Return the previous epoch (unless the current epoch is ``GENESIS_EPOCH``).
    52  //    """
    53  //    current_epoch = get_current_epoch(state)
    54  //    return GENESIS_EPOCH if current_epoch == GENESIS_EPOCH else Epoch(current_epoch - 1)
    55  func PrevEpoch(state iface.ReadOnlyBeaconState) types.Epoch {
    56  	currentEpoch := CurrentEpoch(state)
    57  	if currentEpoch == 0 {
    58  		return 0
    59  	}
    60  	return currentEpoch - 1
    61  }
    62  
    63  // NextEpoch returns the next epoch number calculated from
    64  // the slot number stored in beacon state.
    65  func NextEpoch(state iface.ReadOnlyBeaconState) types.Epoch {
    66  	return SlotToEpoch(state.Slot()) + 1
    67  }
    68  
    69  // StartSlot returns the first slot number of the
    70  // current epoch.
    71  //
    72  // Spec pseudocode definition:
    73  //  def compute_start_slot_at_epoch(epoch: Epoch) -> Slot:
    74  //    """
    75  //    Return the start slot of ``epoch``.
    76  //    """
    77  //    return Slot(epoch * SLOTS_PER_EPOCH)
    78  func StartSlot(epoch types.Epoch) (types.Slot, error) {
    79  	slot, err := params.BeaconConfig().SlotsPerEpoch.SafeMul(uint64(epoch))
    80  	if err != nil {
    81  		return slot, errors.Errorf("start slot calculation overflows: %v", err)
    82  	}
    83  	return slot, nil
    84  }
    85  
    86  // EndSlot returns the last slot number of the
    87  // current epoch.
    88  func EndSlot(epoch types.Epoch) (types.Slot, error) {
    89  	if epoch == math.MaxUint64 {
    90  		return 0, errors.New("start slot calculation overflows")
    91  	}
    92  	slot, err := StartSlot(epoch + 1)
    93  	if err != nil {
    94  		return 0, err
    95  	}
    96  	return slot - 1, nil
    97  }
    98  
    99  // IsEpochStart returns true if the given slot number is an epoch starting slot
   100  // number.
   101  func IsEpochStart(slot types.Slot) bool {
   102  	return slot%params.BeaconConfig().SlotsPerEpoch == 0
   103  }
   104  
   105  // IsEpochEnd returns true if the given slot number is an epoch ending slot
   106  // number.
   107  func IsEpochEnd(slot types.Slot) bool {
   108  	return IsEpochStart(slot + 1)
   109  }
   110  
   111  // SlotsSinceEpochStarts returns number of slots since the start of the epoch.
   112  func SlotsSinceEpochStarts(slot types.Slot) types.Slot {
   113  	return slot % params.BeaconConfig().SlotsPerEpoch
   114  }
   115  
   116  // VerifySlotTime validates the input slot is not from the future.
   117  func VerifySlotTime(genesisTime uint64, slot types.Slot, timeTolerance time.Duration) error {
   118  	slotTime, err := SlotToTime(genesisTime, slot)
   119  	if err != nil {
   120  		return err
   121  	}
   122  
   123  	// Defensive check to ensure unreasonable slots are rejected
   124  	// straight away.
   125  	if err := ValidateSlotClock(slot, genesisTime); err != nil {
   126  		return err
   127  	}
   128  
   129  	currentTime := timeutils.Now()
   130  	diff := slotTime.Sub(currentTime)
   131  
   132  	if diff > timeTolerance {
   133  		return fmt.Errorf("could not process slot from the future, slot time %s > current time %s", slotTime, currentTime)
   134  	}
   135  	return nil
   136  }
   137  
   138  // SlotToTime takes the given slot and genesis time to determine the start time of the slot.
   139  func SlotToTime(genesisTimeSec uint64, slot types.Slot) (time.Time, error) {
   140  	timeSinceGenesis, err := slot.SafeMul(params.BeaconConfig().SecondsPerSlot)
   141  	if err != nil {
   142  		return time.Unix(0, 0), fmt.Errorf("slot (%d) is in the far distant future: %w", slot, err)
   143  	}
   144  	sTime, err := timeSinceGenesis.SafeAdd(genesisTimeSec)
   145  	if err != nil {
   146  		return time.Unix(0, 0), fmt.Errorf("slot (%d) is in the far distant future: %w", slot, err)
   147  	}
   148  	return time.Unix(int64(sTime), 0), nil
   149  }
   150  
   151  // SlotsSince computes the number of time slots that have occurred since the given timestamp.
   152  func SlotsSince(time time.Time) types.Slot {
   153  	return CurrentSlot(uint64(time.Unix()))
   154  }
   155  
   156  // CurrentSlot returns the current slot as determined by the local clock and
   157  // provided genesis time.
   158  func CurrentSlot(genesisTimeSec uint64) types.Slot {
   159  	now := timeutils.Now().Unix()
   160  	genesis := int64(genesisTimeSec)
   161  	if now < genesis {
   162  		return 0
   163  	}
   164  	return types.Slot(uint64(now-genesis) / params.BeaconConfig().SecondsPerSlot)
   165  }
   166  
   167  // ValidateSlotClock validates a provided slot against the local
   168  // clock to ensure slots that are unreasonable are returned with
   169  // an error.
   170  func ValidateSlotClock(slot types.Slot, genesisTimeSec uint64) error {
   171  	maxPossibleSlot := CurrentSlot(genesisTimeSec).Add(MaxSlotBuffer)
   172  	// Defensive check to ensure that we only process slots up to a hard limit
   173  	// from our local clock.
   174  	if slot > maxPossibleSlot {
   175  		return fmt.Errorf("slot %d > %d which exceeds max allowed value relative to the local clock", slot, maxPossibleSlot)
   176  	}
   177  	return nil
   178  }
   179  
   180  // RoundUpToNearestEpoch rounds up the provided slot value to the nearest epoch.
   181  func RoundUpToNearestEpoch(slot types.Slot) types.Slot {
   182  	if slot%params.BeaconConfig().SlotsPerEpoch != 0 {
   183  		slot -= slot % params.BeaconConfig().SlotsPerEpoch
   184  		slot += params.BeaconConfig().SlotsPerEpoch
   185  	}
   186  	return slot
   187  }
   188  
   189  // VotingPeriodStartTime returns the current voting period's start time
   190  // depending on the provided genesis and current slot.
   191  func VotingPeriodStartTime(genesis uint64, slot types.Slot) uint64 {
   192  	slots := params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().EpochsPerEth1VotingPeriod))
   193  	startTime := uint64((slot - slot.ModSlot(slots)).Mul(params.BeaconConfig().SecondsPerSlot))
   194  	return genesis + startTime
   195  }
   196  
   197  // PrevSlot returns previous slot, with an exception in slot 0 to prevent underflow.
   198  func PrevSlot(slot types.Slot) types.Slot {
   199  	if slot > 0 {
   200  		return slot.Sub(1)
   201  	}
   202  	return 0
   203  }