github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/sync/validate_beacon_attestation.go (about) 1 package sync 2 3 import ( 4 "context" 5 "fmt" 6 "reflect" 7 "strings" 8 9 "github.com/libp2p/go-libp2p-core/peer" 10 pubsub "github.com/libp2p/go-libp2p-pubsub" 11 types "github.com/prysmaticlabs/eth2-types" 12 "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" 13 "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" 14 "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation" 15 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 16 "github.com/prysmaticlabs/prysm/beacon-chain/p2p" 17 iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface" 18 eth "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 19 "github.com/prysmaticlabs/prysm/shared/bytesutil" 20 "github.com/prysmaticlabs/prysm/shared/traceutil" 21 "go.opencensus.io/trace" 22 ) 23 24 // Validation 25 // - The block being voted for (attestation.data.beacon_block_root) passes validation. 26 // - The attestation's committee index (attestation.data.index) is for the correct subnet. 27 // - The attestation is unaggregated -- that is, it has exactly one participating validator (len(get_attesting_indices(state, attestation.data, attestation.aggregation_bits)) == 1). 28 // - attestation.data.slot is within the last ATTESTATION_PROPAGATION_SLOT_RANGE slots (attestation.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= attestation.data.slot). 29 // - The signature of attestation is valid. 30 func (s *Service) validateCommitteeIndexBeaconAttestation(ctx context.Context, pid peer.ID, msg *pubsub.Message) pubsub.ValidationResult { 31 if pid == s.cfg.P2P.PeerID() { 32 return pubsub.ValidationAccept 33 } 34 // Attestation processing requires the target block to be present in the database, so we'll skip 35 // validating or processing attestations until fully synced. 36 if s.cfg.InitialSync.Syncing() { 37 return pubsub.ValidationIgnore 38 } 39 ctx, span := trace.StartSpan(ctx, "sync.validateCommitteeIndexBeaconAttestation") 40 defer span.End() 41 42 if msg.Topic == nil { 43 return pubsub.ValidationReject 44 } 45 46 // Override topic for decoding. 47 originalTopic := msg.Topic 48 format := p2p.GossipTypeMapping[reflect.TypeOf(ð.Attestation{})] 49 msg.Topic = &format 50 51 m, err := s.decodePubsubMessage(msg) 52 if err != nil { 53 log.WithError(err).Debug("Could not decode message") 54 traceutil.AnnotateError(span, err) 55 return pubsub.ValidationReject 56 } 57 // Restore topic. 58 msg.Topic = originalTopic 59 60 att, ok := m.(*eth.Attestation) 61 if !ok { 62 return pubsub.ValidationReject 63 } 64 65 if err := helpers.ValidateNilAttestation(att); err != nil { 66 return pubsub.ValidationReject 67 } 68 69 // Broadcast the unaggregated attestation on a feed to notify other services in the beacon node 70 // of a received unaggregated attestation. 71 s.cfg.AttestationNotifier.OperationFeed().Send(&feed.Event{ 72 Type: operation.UnaggregatedAttReceived, 73 Data: &operation.UnAggregatedAttReceivedData{ 74 Attestation: att, 75 }, 76 }) 77 78 // Attestation's slot is within ATTESTATION_PROPAGATION_SLOT_RANGE and early attestation 79 // processing tolerance. 80 if err := helpers.ValidateAttestationTime(att.Data.Slot, s.cfg.Chain.GenesisTime(), 81 earlyAttestationProcessingTolerance); err != nil { 82 traceutil.AnnotateError(span, err) 83 return pubsub.ValidationIgnore 84 } 85 if err := helpers.ValidateSlotTargetEpoch(att.Data); err != nil { 86 return pubsub.ValidationReject 87 } 88 89 // Verify this the first attestation received for the participating validator for the slot. 90 if s.hasSeenCommitteeIndicesSlot(att.Data.Slot, att.Data.CommitteeIndex, att.AggregationBits) { 91 return pubsub.ValidationIgnore 92 } 93 94 // Reject an attestation if it references an invalid block. 95 if s.hasBadBlock(bytesutil.ToBytes32(att.Data.BeaconBlockRoot)) || 96 s.hasBadBlock(bytesutil.ToBytes32(att.Data.Target.Root)) || 97 s.hasBadBlock(bytesutil.ToBytes32(att.Data.Source.Root)) { 98 return pubsub.ValidationReject 99 } 100 101 // Verify the block being voted and the processed state is in DB and. The block should have passed validation if it's in the DB. 102 blockRoot := bytesutil.ToBytes32(att.Data.BeaconBlockRoot) 103 if !s.hasBlockAndState(ctx, blockRoot) { 104 // A node doesn't have the block, it'll request from peer while saving the pending attestation to a queue. 105 s.savePendingAtt(ð.SignedAggregateAttestationAndProof{Message: ð.AggregateAttestationAndProof{Aggregate: att}}) 106 return pubsub.ValidationIgnore 107 } 108 109 if err := s.cfg.Chain.VerifyFinalizedConsistency(ctx, att.Data.BeaconBlockRoot); err != nil { 110 traceutil.AnnotateError(span, err) 111 return pubsub.ValidationReject 112 } 113 if err := s.cfg.Chain.VerifyLmdFfgConsistency(ctx, att); err != nil { 114 traceutil.AnnotateError(span, err) 115 return pubsub.ValidationReject 116 } 117 118 preState, err := s.cfg.Chain.AttestationPreState(ctx, att) 119 if err != nil { 120 log.WithError(err).Error("Could not to retrieve pre state") 121 traceutil.AnnotateError(span, err) 122 return pubsub.ValidationIgnore 123 } 124 125 validationRes := s.validateUnaggregatedAttTopic(ctx, att, preState, *originalTopic) 126 if validationRes != pubsub.ValidationAccept { 127 return validationRes 128 } 129 130 validationRes = s.validateUnaggregatedAttWithState(ctx, att, preState) 131 if validationRes != pubsub.ValidationAccept { 132 return validationRes 133 } 134 135 s.setSeenCommitteeIndicesSlot(att.Data.Slot, att.Data.CommitteeIndex, att.AggregationBits) 136 137 msg.ValidatorData = att 138 139 return pubsub.ValidationAccept 140 } 141 142 // This validates beacon unaggregated attestation has correct topic string. 143 func (s *Service) validateUnaggregatedAttTopic(ctx context.Context, a *eth.Attestation, bs iface.ReadOnlyBeaconState, t string) pubsub.ValidationResult { 144 ctx, span := trace.StartSpan(ctx, "sync.validateUnaggregatedAttTopic") 145 defer span.End() 146 147 valCount, err := helpers.ActiveValidatorCount(bs, helpers.SlotToEpoch(a.Data.Slot)) 148 if err != nil { 149 log.WithError(err).Error("Could not retrieve active validator count") 150 traceutil.AnnotateError(span, err) 151 return pubsub.ValidationIgnore 152 } 153 count := helpers.SlotCommitteeCount(valCount) 154 if uint64(a.Data.CommitteeIndex) > count { 155 return pubsub.ValidationReject 156 } 157 subnet := helpers.ComputeSubnetForAttestation(valCount, a) 158 format := p2p.GossipTypeMapping[reflect.TypeOf(ð.Attestation{})] 159 digest, err := s.forkDigest() 160 if err != nil { 161 log.WithError(err).Error("Could not compute fork digest") 162 traceutil.AnnotateError(span, err) 163 return pubsub.ValidationIgnore 164 } 165 if !strings.HasPrefix(t, fmt.Sprintf(format, digest, subnet)) { 166 return pubsub.ValidationReject 167 } 168 169 return pubsub.ValidationAccept 170 } 171 172 // This validates beacon unaggregated attestation using the given state, the validation consists of bitfield length and count consistency 173 // and signature verification. 174 func (s *Service) validateUnaggregatedAttWithState(ctx context.Context, a *eth.Attestation, bs iface.ReadOnlyBeaconState) pubsub.ValidationResult { 175 ctx, span := trace.StartSpan(ctx, "sync.validateUnaggregatedAttWithState") 176 defer span.End() 177 178 committee, err := helpers.BeaconCommitteeFromState(bs, a.Data.Slot, a.Data.CommitteeIndex) 179 if err != nil { 180 traceutil.AnnotateError(span, err) 181 return pubsub.ValidationIgnore 182 } 183 184 // Verify number of aggregation bits matches the committee size. 185 if err := helpers.VerifyBitfieldLength(a.AggregationBits, uint64(len(committee))); err != nil { 186 return pubsub.ValidationReject 187 } 188 189 // Attestation must be unaggregated and the bit index must exist in the range of committee indices. 190 // Note: The Ethereum Beacon Chain spec suggests (len(get_attesting_indices(state, attestation.data, attestation.aggregation_bits)) == 1) 191 // however this validation can be achieved without use of get_attesting_indices which is an O(n) lookup. 192 if a.AggregationBits.Count() != 1 || a.AggregationBits.BitIndices()[0] >= len(committee) { 193 return pubsub.ValidationReject 194 } 195 196 if err := blocks.VerifyAttestationSignature(ctx, bs, a); err != nil { 197 log.WithError(err).Debug("Could not verify attestation") 198 traceutil.AnnotateError(span, err) 199 return pubsub.ValidationReject 200 } 201 202 return pubsub.ValidationAccept 203 } 204 205 // Returns true if the attestation was already seen for the participating validator for the slot. 206 func (s *Service) hasSeenCommitteeIndicesSlot(slot types.Slot, committeeID types.CommitteeIndex, aggregateBits []byte) bool { 207 s.seenUnAggregatedAttestationLock.RLock() 208 defer s.seenUnAggregatedAttestationLock.RUnlock() 209 b := append(bytesutil.Bytes32(uint64(slot)), bytesutil.Bytes32(uint64(committeeID))...) 210 b = append(b, aggregateBits...) 211 _, seen := s.seenUnAggregatedAttestationCache.Get(string(b)) 212 return seen 213 } 214 215 // Set committee's indices and slot as seen for incoming attestations. 216 func (s *Service) setSeenCommitteeIndicesSlot(slot types.Slot, committeeID types.CommitteeIndex, aggregateBits []byte) { 217 s.seenUnAggregatedAttestationLock.Lock() 218 defer s.seenUnAggregatedAttestationLock.Unlock() 219 b := append(bytesutil.Bytes32(uint64(slot)), bytesutil.Bytes32(uint64(committeeID))...) 220 b = append(b, aggregateBits...) 221 s.seenUnAggregatedAttestationCache.Add(string(b), true) 222 } 223 224 // hasBlockAndState returns true if the beacon node knows about a block and associated state in the 225 // database or cache. 226 func (s *Service) hasBlockAndState(ctx context.Context, blockRoot [32]byte) bool { 227 hasStateSummary := s.cfg.DB.HasStateSummary(ctx, blockRoot) 228 hasState := hasStateSummary || s.cfg.DB.HasState(ctx, blockRoot) 229 hasBlock := s.cfg.Chain.HasInitSyncBlock(blockRoot) || s.cfg.DB.HasBlock(ctx, blockRoot) 230 return hasState && hasBlock 231 }