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 }