github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/sync/validate_aggregate_proof.go (about) 1 package sync 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/libp2p/go-libp2p-core/peer" 8 pubsub "github.com/libp2p/go-libp2p-pubsub" 9 "github.com/pkg/errors" 10 types "github.com/prysmaticlabs/eth2-types" 11 "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks" 12 "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" 13 "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation" 14 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 15 "github.com/prysmaticlabs/prysm/beacon-chain/core/state" 16 iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface" 17 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 18 "github.com/prysmaticlabs/prysm/shared/bls" 19 "github.com/prysmaticlabs/prysm/shared/bytesutil" 20 "github.com/prysmaticlabs/prysm/shared/params" 21 "github.com/prysmaticlabs/prysm/shared/traceutil" 22 "go.opencensus.io/trace" 23 ) 24 25 // validateAggregateAndProof verifies the aggregated signature and the selection proof is valid before forwarding to the 26 // network and downstream services. 27 func (s *Service) validateAggregateAndProof(ctx context.Context, pid peer.ID, msg *pubsub.Message) pubsub.ValidationResult { 28 if pid == s.cfg.P2P.PeerID() { 29 return pubsub.ValidationAccept 30 } 31 32 ctx, span := trace.StartSpan(ctx, "sync.validateAggregateAndProof") 33 defer span.End() 34 35 // To process the following it requires the recent blocks to be present in the database, so we'll skip 36 // validating or processing aggregated attestations until fully synced. 37 if s.cfg.InitialSync.Syncing() { 38 return pubsub.ValidationIgnore 39 } 40 41 raw, err := s.decodePubsubMessage(msg) 42 if err != nil { 43 log.WithError(err).Debug("Could not decode message") 44 traceutil.AnnotateError(span, err) 45 return pubsub.ValidationReject 46 } 47 m, ok := raw.(*ethpb.SignedAggregateAttestationAndProof) 48 if !ok { 49 return pubsub.ValidationReject 50 } 51 if m.Message == nil { 52 return pubsub.ValidationReject 53 } 54 if err := helpers.ValidateNilAttestation(m.Message.Aggregate); err != nil { 55 return pubsub.ValidationReject 56 } 57 58 // Broadcast the aggregated attestation on a feed to notify other services in the beacon node 59 // of a received aggregated attestation. 60 s.cfg.AttestationNotifier.OperationFeed().Send(&feed.Event{ 61 Type: operation.AggregatedAttReceived, 62 Data: &operation.AggregatedAttReceivedData{ 63 Attestation: m.Message, 64 }, 65 }) 66 67 if err := helpers.ValidateSlotTargetEpoch(m.Message.Aggregate.Data); err != nil { 68 return pubsub.ValidationReject 69 } 70 71 // Attestation's slot is within ATTESTATION_PROPAGATION_SLOT_RANGE and early attestation 72 // processing tolerance. 73 if err := helpers.ValidateAttestationTime(m.Message.Aggregate.Data.Slot, s.cfg.Chain.GenesisTime(), 74 earlyAttestationProcessingTolerance); err != nil { 75 traceutil.AnnotateError(span, err) 76 return pubsub.ValidationIgnore 77 } 78 79 // Verify this is the first aggregate received from the aggregator with index and slot. 80 if s.hasSeenAggregatorIndexEpoch(m.Message.Aggregate.Data.Target.Epoch, m.Message.AggregatorIndex) { 81 return pubsub.ValidationIgnore 82 } 83 // Check that the block being voted on isn't invalid. 84 if s.hasBadBlock(bytesutil.ToBytes32(m.Message.Aggregate.Data.BeaconBlockRoot)) || 85 s.hasBadBlock(bytesutil.ToBytes32(m.Message.Aggregate.Data.Target.Root)) || 86 s.hasBadBlock(bytesutil.ToBytes32(m.Message.Aggregate.Data.Source.Root)) { 87 return pubsub.ValidationReject 88 } 89 90 // Verify aggregate attestation has not already been seen via aggregate gossip, within a block, or through the creation locally. 91 seen, err := s.cfg.AttPool.HasAggregatedAttestation(m.Message.Aggregate) 92 if err != nil { 93 traceutil.AnnotateError(span, err) 94 return pubsub.ValidationIgnore 95 } 96 if seen { 97 return pubsub.ValidationIgnore 98 } 99 if !s.validateBlockInAttestation(ctx, m) { 100 return pubsub.ValidationIgnore 101 } 102 103 validationRes := s.validateAggregatedAtt(ctx, m) 104 if validationRes != pubsub.ValidationAccept { 105 return validationRes 106 } 107 108 s.setAggregatorIndexEpochSeen(m.Message.Aggregate.Data.Target.Epoch, m.Message.AggregatorIndex) 109 110 msg.ValidatorData = m 111 112 return pubsub.ValidationAccept 113 } 114 115 func (s *Service) validateAggregatedAtt(ctx context.Context, signed *ethpb.SignedAggregateAttestationAndProof) pubsub.ValidationResult { 116 ctx, span := trace.StartSpan(ctx, "sync.validateAggregatedAtt") 117 defer span.End() 118 119 // Verify attestation target root is consistent with the head root. 120 // This verification is not in the spec, however we guard against it as it opens us up 121 // to weird edge cases during verification. The attestation technically could be used to add value to a block, 122 // but it's invalid in the spirit of the protocol. Here we choose safety over profit. 123 if err := s.cfg.Chain.VerifyLmdFfgConsistency(ctx, signed.Message.Aggregate); err != nil { 124 traceutil.AnnotateError(span, err) 125 return pubsub.ValidationReject 126 } 127 128 // Verify current finalized checkpoint is an ancestor of the block defined by the attestation's beacon block root. 129 if err := s.cfg.Chain.VerifyFinalizedConsistency(ctx, signed.Message.Aggregate.Data.BeaconBlockRoot); err != nil { 130 traceutil.AnnotateError(span, err) 131 return pubsub.ValidationReject 132 } 133 134 bs, err := s.cfg.Chain.AttestationPreState(ctx, signed.Message.Aggregate) 135 if err != nil { 136 traceutil.AnnotateError(span, err) 137 return pubsub.ValidationIgnore 138 } 139 140 attSlot := signed.Message.Aggregate.Data.Slot 141 // Only advance state if different epoch as the committee can only change on an epoch transition. 142 if helpers.SlotToEpoch(attSlot) > helpers.SlotToEpoch(bs.Slot()) { 143 startSlot, err := helpers.StartSlot(helpers.SlotToEpoch(attSlot)) 144 if err != nil { 145 return pubsub.ValidationIgnore 146 } 147 bs, err = state.ProcessSlots(ctx, bs, startSlot) 148 if err != nil { 149 traceutil.AnnotateError(span, err) 150 return pubsub.ValidationIgnore 151 } 152 } 153 154 // Verify validator index is within the beacon committee. 155 if err := validateIndexInCommittee(ctx, bs, signed.Message.Aggregate, signed.Message.AggregatorIndex); err != nil { 156 traceutil.AnnotateError(span, errors.Wrapf(err, "Could not validate index in committee")) 157 return pubsub.ValidationReject 158 } 159 160 // Verify selection proof reflects to the right validator. 161 selectionSigSet, err := validateSelectionIndex(ctx, bs, signed.Message.Aggregate.Data, signed.Message.AggregatorIndex, signed.Message.SelectionProof) 162 if err != nil { 163 traceutil.AnnotateError(span, errors.Wrapf(err, "Could not validate selection for validator %d", signed.Message.AggregatorIndex)) 164 return pubsub.ValidationReject 165 } 166 167 // Verify selection signature, aggregator signature and attestation signature are valid. 168 // We use batch verify here to save compute. 169 aggregatorSigSet, err := aggSigSet(bs, signed) 170 if err != nil { 171 traceutil.AnnotateError(span, errors.Wrapf(err, "Could not get aggregator sig set %d", signed.Message.AggregatorIndex)) 172 return pubsub.ValidationIgnore 173 } 174 attSigSet, err := blocks.AttestationSignatureSet(ctx, bs, []*ethpb.Attestation{signed.Message.Aggregate}) 175 if err != nil { 176 traceutil.AnnotateError(span, errors.Wrapf(err, "Could not verify aggregator signature %d", signed.Message.AggregatorIndex)) 177 return pubsub.ValidationIgnore 178 } 179 set := bls.NewSet() 180 set.Join(selectionSigSet).Join(aggregatorSigSet).Join(attSigSet) 181 valid, err := set.Verify() 182 if err != nil { 183 traceutil.AnnotateError(span, errors.Errorf("Could not join signature set")) 184 return pubsub.ValidationIgnore 185 } 186 if !valid { 187 traceutil.AnnotateError(span, errors.Errorf("Could not verify selection or aggregator or attestation signature")) 188 return pubsub.ValidationReject 189 } 190 191 return pubsub.ValidationAccept 192 } 193 194 func (s *Service) validateBlockInAttestation(ctx context.Context, satt *ethpb.SignedAggregateAttestationAndProof) bool { 195 a := satt.Message 196 // Verify the block being voted and the processed state is in DB. The block should have passed validation if it's in the DB. 197 blockRoot := bytesutil.ToBytes32(a.Aggregate.Data.BeaconBlockRoot) 198 if !s.hasBlockAndState(ctx, blockRoot) { 199 // A node doesn't have the block, it'll request from peer while saving the pending attestation to a queue. 200 s.savePendingAtt(satt) 201 return false 202 } 203 return true 204 } 205 206 // Returns true if the node has received aggregate for the aggregator with index and target epoch. 207 func (s *Service) hasSeenAggregatorIndexEpoch(epoch types.Epoch, aggregatorIndex types.ValidatorIndex) bool { 208 s.seenAggregatedAttestationLock.RLock() 209 defer s.seenAggregatedAttestationLock.RUnlock() 210 b := append(bytesutil.Bytes32(uint64(epoch)), bytesutil.Bytes32(uint64(aggregatorIndex))...) 211 _, seen := s.seenAggregatedAttestationCache.Get(string(b)) 212 return seen 213 } 214 215 // Set aggregate's aggregator index target epoch as seen. 216 func (s *Service) setAggregatorIndexEpochSeen(epoch types.Epoch, aggregatorIndex types.ValidatorIndex) { 217 s.seenAggregatedAttestationLock.Lock() 218 defer s.seenAggregatedAttestationLock.Unlock() 219 b := append(bytesutil.Bytes32(uint64(epoch)), bytesutil.Bytes32(uint64(aggregatorIndex))...) 220 s.seenAggregatedAttestationCache.Add(string(b), true) 221 } 222 223 // This validates the aggregator's index in state is within the beacon committee. 224 func validateIndexInCommittee(ctx context.Context, bs iface.ReadOnlyBeaconState, a *ethpb.Attestation, validatorIndex types.ValidatorIndex) error { 225 ctx, span := trace.StartSpan(ctx, "sync.validateIndexInCommittee") 226 defer span.End() 227 228 committee, err := helpers.BeaconCommitteeFromState(bs, a.Data.Slot, a.Data.CommitteeIndex) 229 if err != nil { 230 return err 231 } 232 var withinCommittee bool 233 for _, i := range committee { 234 if validatorIndex == i { 235 withinCommittee = true 236 break 237 } 238 } 239 if !withinCommittee { 240 return fmt.Errorf("validator index %d is not within the committee: %v", 241 validatorIndex, committee) 242 } 243 return nil 244 } 245 246 // This validates selection proof by validating it's from the correct validator index of the slot. 247 // It does not verify the selection proof, it returns the signature set of selection proof which can be used for batch verify. 248 func validateSelectionIndex( 249 ctx context.Context, 250 bs iface.ReadOnlyBeaconState, 251 data *ethpb.AttestationData, 252 validatorIndex types.ValidatorIndex, 253 proof []byte, 254 ) (*bls.SignatureSet, error) { 255 _, span := trace.StartSpan(ctx, "sync.validateSelectionIndex") 256 defer span.End() 257 258 committee, err := helpers.BeaconCommitteeFromState(bs, data.Slot, data.CommitteeIndex) 259 if err != nil { 260 return nil, err 261 } 262 aggregator, err := helpers.IsAggregator(uint64(len(committee)), proof) 263 if err != nil { 264 return nil, err 265 } 266 if !aggregator { 267 return nil, fmt.Errorf("validator is not an aggregator for slot %d", data.Slot) 268 } 269 270 domain := params.BeaconConfig().DomainSelectionProof 271 epoch := helpers.SlotToEpoch(data.Slot) 272 273 v, err := bs.ValidatorAtIndex(validatorIndex) 274 if err != nil { 275 return nil, err 276 } 277 publicKey, err := bls.PublicKeyFromBytes(v.PublicKey) 278 if err != nil { 279 return nil, err 280 } 281 282 d, err := helpers.Domain(bs.Fork(), epoch, domain, bs.GenesisValidatorRoot()) 283 if err != nil { 284 return nil, err 285 } 286 sszUint := types.SSZUint64(data.Slot) 287 root, err := helpers.ComputeSigningRoot(&sszUint, d) 288 if err != nil { 289 return nil, err 290 } 291 return &bls.SignatureSet{ 292 Signatures: [][]byte{proof}, 293 PublicKeys: []bls.PublicKey{publicKey}, 294 Messages: [][32]byte{root}, 295 }, nil 296 } 297 298 // This returns aggregator signature set which can be used to batch verify. 299 func aggSigSet(s iface.ReadOnlyBeaconState, a *ethpb.SignedAggregateAttestationAndProof) (*bls.SignatureSet, error) { 300 v, err := s.ValidatorAtIndex(a.Message.AggregatorIndex) 301 if err != nil { 302 return nil, err 303 } 304 publicKey, err := bls.PublicKeyFromBytes(v.PublicKey) 305 if err != nil { 306 return nil, err 307 } 308 309 epoch := helpers.SlotToEpoch(a.Message.Aggregate.Data.Slot) 310 d, err := helpers.Domain(s.Fork(), epoch, params.BeaconConfig().DomainAggregateAndProof, s.GenesisValidatorRoot()) 311 if err != nil { 312 return nil, err 313 } 314 root, err := helpers.ComputeSigningRoot(a.Message, d) 315 if err != nil { 316 return nil, err 317 } 318 return &bls.SignatureSet{ 319 Signatures: [][]byte{a.Signature}, 320 PublicKeys: []bls.PublicKey{publicKey}, 321 Messages: [][32]byte{root}, 322 }, nil 323 }