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

     1  package helpers
     2  
     3  import (
     4  	"encoding/binary"
     5  	"errors"
     6  	"fmt"
     7  	"time"
     8  
     9  	types "github.com/prysmaticlabs/eth2-types"
    10  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    11  	"github.com/prysmaticlabs/prysm/shared/bls"
    12  	"github.com/prysmaticlabs/prysm/shared/hashutil"
    13  	"github.com/prysmaticlabs/prysm/shared/params"
    14  	"github.com/prysmaticlabs/prysm/shared/timeutils"
    15  )
    16  
    17  // ValidateNilAttestation checks if any composite field of input attestation is nil.
    18  // Access to these nil fields will result in run time panic,
    19  // it is recommended to run these checks as first line of defense.
    20  func ValidateNilAttestation(attestation *ethpb.Attestation) error {
    21  	if attestation == nil {
    22  		return errors.New("attestation can't be nil")
    23  	}
    24  	if attestation.Data == nil {
    25  		return errors.New("attestation's data can't be nil")
    26  	}
    27  	if attestation.Data.Source == nil {
    28  		return errors.New("attestation's source can't be nil")
    29  	}
    30  	if attestation.Data.Target == nil {
    31  		return errors.New("attestation's target can't be nil")
    32  	}
    33  	if attestation.AggregationBits == nil {
    34  		return errors.New("attestation's bitfield can't be nil")
    35  	}
    36  	return nil
    37  }
    38  
    39  // ValidateSlotTargetEpoch checks if attestation data's epoch matches target checkpoint's epoch.
    40  // It is recommended to run `ValidateNilAttestation` first to ensure `data.Target` can't be nil.
    41  func ValidateSlotTargetEpoch(data *ethpb.AttestationData) error {
    42  	if SlotToEpoch(data.Slot) != data.Target.Epoch {
    43  		return fmt.Errorf("slot %d does not match target epoch %d", data.Slot, data.Target.Epoch)
    44  	}
    45  	return nil
    46  }
    47  
    48  // IsAggregator returns true if the signature is from the input validator. The committee
    49  // count is provided as an argument rather than imported implementation from spec. Having
    50  // committee count as an argument allows cheaper computation at run time.
    51  //
    52  // Spec pseudocode definition:
    53  //   def is_aggregator(state: BeaconState, slot: Slot, index: CommitteeIndex, slot_signature: BLSSignature) -> bool:
    54  //    committee = get_beacon_committee(state, slot, index)
    55  //    modulo = max(1, len(committee) // TARGET_AGGREGATORS_PER_COMMITTEE)
    56  //    return bytes_to_uint64(hash(slot_signature)[0:8]) % modulo == 0
    57  func IsAggregator(committeeCount uint64, slotSig []byte) (bool, error) {
    58  	modulo := uint64(1)
    59  	if committeeCount/params.BeaconConfig().TargetAggregatorsPerCommittee > 1 {
    60  		modulo = committeeCount / params.BeaconConfig().TargetAggregatorsPerCommittee
    61  	}
    62  
    63  	b := hashutil.Hash(slotSig)
    64  	return binary.LittleEndian.Uint64(b[:8])%modulo == 0, nil
    65  }
    66  
    67  // AggregateSignature returns the aggregated signature of the input attestations.
    68  //
    69  // Spec pseudocode definition:
    70  //   def get_aggregate_signature(attestations: Sequence[Attestation]) -> BLSSignature:
    71  //    signatures = [attestation.signature for attestation in attestations]
    72  //    return bls.Aggregate(signatures)
    73  func AggregateSignature(attestations []*ethpb.Attestation) (bls.Signature, error) {
    74  	sigs := make([]bls.Signature, len(attestations))
    75  	var err error
    76  	for i := 0; i < len(sigs); i++ {
    77  		sigs[i], err = bls.SignatureFromBytes(attestations[i].Signature)
    78  		if err != nil {
    79  			return nil, err
    80  		}
    81  	}
    82  	return bls.AggregateSignatures(sigs), nil
    83  }
    84  
    85  // IsAggregated returns true if the attestation is an aggregated attestation,
    86  // false otherwise.
    87  func IsAggregated(attestation *ethpb.Attestation) bool {
    88  	return attestation.AggregationBits.Count() > 1
    89  }
    90  
    91  // ComputeSubnetForAttestation returns the subnet for which the provided attestation will be broadcasted to.
    92  // This differs from the spec definition by instead passing in the active validators indices in the attestation's
    93  // given epoch.
    94  //
    95  // Spec pseudocode definition:
    96  // def compute_subnet_for_attestation(committees_per_slot: uint64, slot: Slot, committee_index: CommitteeIndex) -> uint64:
    97  //    """
    98  //    Compute the correct subnet for an attestation for Phase 0.
    99  //    Note, this mimics expected future behavior where attestations will be mapped to their shard subnet.
   100  //    """
   101  //    slots_since_epoch_start = uint64(slot % SLOTS_PER_EPOCH)
   102  //    committees_since_epoch_start = committees_per_slot * slots_since_epoch_start
   103  //
   104  //    return uint64((committees_since_epoch_start + committee_index) % ATTESTATION_SUBNET_COUNT)
   105  func ComputeSubnetForAttestation(activeValCount uint64, att *ethpb.Attestation) uint64 {
   106  	return ComputeSubnetFromCommitteeAndSlot(activeValCount, att.Data.CommitteeIndex, att.Data.Slot)
   107  }
   108  
   109  // ComputeSubnetFromCommitteeAndSlot is a flattened version of ComputeSubnetForAttestation where we only pass in
   110  // the relevant fields from the attestation as function arguments.
   111  //
   112  // Spec pseudocode definition:
   113  // def compute_subnet_for_attestation(committees_per_slot: uint64, slot: Slot, committee_index: CommitteeIndex) -> uint64:
   114  //    """
   115  //    Compute the correct subnet for an attestation for Phase 0.
   116  //    Note, this mimics expected future behavior where attestations will be mapped to their shard subnet.
   117  //    """
   118  //    slots_since_epoch_start = uint64(slot % SLOTS_PER_EPOCH)
   119  //    committees_since_epoch_start = committees_per_slot * slots_since_epoch_start
   120  //
   121  //    return uint64((committees_since_epoch_start + committee_index) % ATTESTATION_SUBNET_COUNT)
   122  func ComputeSubnetFromCommitteeAndSlot(activeValCount uint64, comIdx types.CommitteeIndex, attSlot types.Slot) uint64 {
   123  	slotSinceStart := SlotsSinceEpochStarts(attSlot)
   124  	comCount := SlotCommitteeCount(activeValCount)
   125  	commsSinceStart := uint64(slotSinceStart.Mul(comCount))
   126  	computedSubnet := (commsSinceStart + uint64(comIdx)) % params.BeaconNetworkConfig().AttestationSubnetCount
   127  	return computedSubnet
   128  }
   129  
   130  // ValidateAttestationTime Validates that the incoming attestation is in the desired time range.
   131  // An attestation is valid only if received within the last ATTESTATION_PROPAGATION_SLOT_RANGE
   132  // slots.
   133  //
   134  // Example:
   135  //   ATTESTATION_PROPAGATION_SLOT_RANGE = 5
   136  //   clockDisparity = 24 seconds
   137  //   current_slot = 100
   138  //   invalid_attestation_slot = 92
   139  //   invalid_attestation_slot = 103
   140  //   valid_attestation_slot = 98
   141  //   valid_attestation_slot = 101
   142  // In the attestation must be within the range of 95 to 102 in the example above.
   143  func ValidateAttestationTime(attSlot types.Slot, genesisTime time.Time, clockDisparity time.Duration) error {
   144  	if err := ValidateSlotClock(attSlot, uint64(genesisTime.Unix())); err != nil {
   145  		return err
   146  	}
   147  	attTime, err := SlotToTime(uint64(genesisTime.Unix()), attSlot)
   148  	if err != nil {
   149  		return err
   150  	}
   151  	currentSlot := SlotsSince(genesisTime)
   152  
   153  	// When receiving an attestation, it can be from the future.
   154  	// so the upper bounds is set to now + clockDisparity(SECONDS_PER_SLOT * 2).
   155  	// But when sending an attestation, it should not be in future slot.
   156  	// so the upper bounds is set to now + clockDisparity(MAXIMUM_GOSSIP_CLOCK_DISPARITY).
   157  	upperBounds := timeutils.Now().Add(clockDisparity)
   158  
   159  	// An attestation cannot be older than the current slot - attestation propagation slot range
   160  	// with a minor tolerance for peer clock disparity.
   161  	lowerBoundsSlot := types.Slot(0)
   162  	if currentSlot > params.BeaconNetworkConfig().AttestationPropagationSlotRange {
   163  		lowerBoundsSlot = currentSlot - params.BeaconNetworkConfig().AttestationPropagationSlotRange
   164  	}
   165  	lowerTime, err := SlotToTime(uint64(genesisTime.Unix()), lowerBoundsSlot)
   166  	if err != nil {
   167  		return err
   168  	}
   169  	lowerBounds := lowerTime.Add(-clockDisparity)
   170  
   171  	// Verify attestation slot within the time range.
   172  	if attTime.Before(lowerBounds) || attTime.After(upperBounds) {
   173  		return fmt.Errorf(
   174  			"attestation slot %d not within attestation propagation range of %d to %d (current slot)",
   175  			attSlot,
   176  			lowerBoundsSlot,
   177  			currentSlot,
   178  		)
   179  	}
   180  	return nil
   181  }
   182  
   183  // VerifyCheckpointEpoch is within current epoch and previous epoch
   184  // with respect to current time. Returns true if it's within, false if it's not.
   185  func VerifyCheckpointEpoch(c *ethpb.Checkpoint, genesis time.Time) bool {
   186  	now := uint64(timeutils.Now().Unix())
   187  	genesisTime := uint64(genesis.Unix())
   188  	currentSlot := types.Slot((now - genesisTime) / params.BeaconConfig().SecondsPerSlot)
   189  	currentEpoch := SlotToEpoch(currentSlot)
   190  
   191  	var prevEpoch types.Epoch
   192  	if currentEpoch > 1 {
   193  		prevEpoch = currentEpoch - 1
   194  	}
   195  
   196  	if c.Epoch != prevEpoch && c.Epoch != currentEpoch {
   197  		return false
   198  	}
   199  
   200  	return true
   201  }