github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/sync/pending_attestations_queue.go (about) 1 package sync 2 3 import ( 4 "context" 5 "encoding/hex" 6 "sync" 7 8 pubsub "github.com/libp2p/go-libp2p-pubsub" 9 types "github.com/prysmaticlabs/eth2-types" 10 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 11 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 12 "github.com/prysmaticlabs/prysm/shared/bytesutil" 13 "github.com/prysmaticlabs/prysm/shared/params" 14 "github.com/prysmaticlabs/prysm/shared/rand" 15 "github.com/prysmaticlabs/prysm/shared/runutil" 16 "github.com/prysmaticlabs/prysm/shared/slotutil" 17 "github.com/sirupsen/logrus" 18 "go.opencensus.io/trace" 19 ) 20 21 // This defines how often a node cleans up and processes pending attestations in the queue. 22 var processPendingAttsPeriod = slotutil.DivideSlotBy(2 /* twice per slot */) 23 24 // This processes pending attestation queues on every `processPendingAttsPeriod`. 25 func (s *Service) processPendingAttsQueue() { 26 // Prevents multiple queue processing goroutines (invoked by RunEvery) from contending for data. 27 mutex := new(sync.Mutex) 28 runutil.RunEvery(s.ctx, processPendingAttsPeriod, func() { 29 mutex.Lock() 30 if err := s.processPendingAtts(s.ctx); err != nil { 31 log.WithError(err).Debugf("Could not process pending attestation: %v", err) 32 } 33 mutex.Unlock() 34 }) 35 } 36 37 // This defines how pending attestations are processed. It contains features: 38 // 1. Clean up invalid pending attestations from the queue. 39 // 2. Check if pending attestations can be processed when the block has arrived. 40 // 3. Request block from a random peer if unable to proceed step 2. 41 func (s *Service) processPendingAtts(ctx context.Context) error { 42 ctx, span := trace.StartSpan(ctx, "processPendingAtts") 43 defer span.End() 44 45 // Before a node processes pending attestations queue, it verifies 46 // the attestations in the queue are still valid. Attestations will 47 // be deleted from the queue if invalid (ie. getting staled from falling too many slots behind). 48 s.validatePendingAtts(ctx, s.cfg.Chain.CurrentSlot()) 49 50 s.pendingAttsLock.RLock() 51 roots := make([][32]byte, 0, len(s.blkRootToPendingAtts)) 52 for br := range s.blkRootToPendingAtts { 53 roots = append(roots, br) 54 } 55 s.pendingAttsLock.RUnlock() 56 57 var pendingRoots [][32]byte 58 randGen := rand.NewGenerator() 59 for _, bRoot := range roots { 60 s.pendingAttsLock.RLock() 61 attestations := s.blkRootToPendingAtts[bRoot] 62 s.pendingAttsLock.RUnlock() 63 // has the pending attestation's missing block arrived and the node processed block yet? 64 if s.cfg.DB.HasBlock(ctx, bRoot) && (s.cfg.DB.HasState(ctx, bRoot) || s.cfg.DB.HasStateSummary(ctx, bRoot)) { 65 for _, signedAtt := range attestations { 66 att := signedAtt.Message 67 // The pending attestations can arrive in both aggregated and unaggregated forms, 68 // each from has distinct validation steps. 69 if helpers.IsAggregated(att.Aggregate) { 70 // Save the pending aggregated attestation to the pool if it passes the aggregated 71 // validation steps. 72 aggValid := s.validateAggregatedAtt(ctx, signedAtt) == pubsub.ValidationAccept 73 if s.validateBlockInAttestation(ctx, signedAtt) && aggValid { 74 if err := s.cfg.AttPool.SaveAggregatedAttestation(att.Aggregate); err != nil { 75 log.WithError(err).Debug("Could not save aggregate attestation") 76 continue 77 } 78 s.setAggregatorIndexEpochSeen(att.Aggregate.Data.Target.Epoch, att.AggregatorIndex) 79 80 // Broadcasting the signed attestation again once a node is able to process it. 81 if err := s.cfg.P2P.Broadcast(ctx, signedAtt); err != nil { 82 log.WithError(err).Debug("Could not broadcast") 83 } 84 } 85 } else { 86 // This is an important validation before retrieving attestation pre state to defend against 87 // attestation's target intentionally reference checkpoint that's long ago. 88 // Verify current finalized checkpoint is an ancestor of the block defined by the attestation's beacon block root. 89 if err := s.cfg.Chain.VerifyFinalizedConsistency(ctx, att.Aggregate.Data.BeaconBlockRoot); err != nil { 90 log.WithError(err).Debug("Could not verify finalized consistency") 91 continue 92 } 93 if err := s.cfg.Chain.VerifyLmdFfgConsistency(ctx, att.Aggregate); err != nil { 94 log.WithError(err).Debug("Could not verify FFG consistency") 95 continue 96 } 97 preState, err := s.cfg.Chain.AttestationPreState(ctx, att.Aggregate) 98 if err != nil { 99 log.WithError(err).Debug("Could not retrieve attestation prestate") 100 continue 101 } 102 103 valid := s.validateUnaggregatedAttWithState(ctx, att.Aggregate, preState) 104 if valid == pubsub.ValidationAccept { 105 if err := s.cfg.AttPool.SaveUnaggregatedAttestation(att.Aggregate); err != nil { 106 log.WithError(err).Debug("Could not save unaggregated attestation") 107 continue 108 } 109 s.setSeenCommitteeIndicesSlot(att.Aggregate.Data.Slot, att.Aggregate.Data.CommitteeIndex, att.Aggregate.AggregationBits) 110 111 valCount, err := helpers.ActiveValidatorCount(preState, helpers.SlotToEpoch(att.Aggregate.Data.Slot)) 112 if err != nil { 113 log.WithError(err).Debug("Could not retrieve active validator count") 114 continue 115 } 116 // Broadcasting the signed attestation again once a node is able to process it. 117 if err := s.cfg.P2P.BroadcastAttestation(ctx, helpers.ComputeSubnetForAttestation(valCount, signedAtt.Message.Aggregate), signedAtt.Message.Aggregate); err != nil { 118 log.WithError(err).Debug("Could not broadcast") 119 } 120 } 121 } 122 } 123 log.WithFields(logrus.Fields{ 124 "blockRoot": hex.EncodeToString(bytesutil.Trunc(bRoot[:])), 125 "pendingAttsCount": len(attestations), 126 }).Debug("Verified and saved pending attestations to pool") 127 128 // Delete the missing block root key from pending attestation queue so a node will not request for the block again. 129 s.pendingAttsLock.Lock() 130 delete(s.blkRootToPendingAtts, bRoot) 131 s.pendingAttsLock.Unlock() 132 } else { 133 // Pending attestation's missing block has not arrived yet. 134 log.WithFields(logrus.Fields{ 135 "currentSlot": s.cfg.Chain.CurrentSlot(), 136 "attSlot": attestations[0].Message.Aggregate.Data.Slot, 137 "attCount": len(attestations), 138 "blockRoot": hex.EncodeToString(bytesutil.Trunc(bRoot[:])), 139 }).Debug("Requesting block for pending attestation") 140 pendingRoots = append(pendingRoots, bRoot) 141 } 142 } 143 return s.sendBatchRootRequest(ctx, pendingRoots, randGen) 144 } 145 146 // This defines how pending attestations is saved in the map. The key is the 147 // root of the missing block. The value is the list of pending attestations 148 // that voted for that block root. 149 func (s *Service) savePendingAtt(att *ethpb.SignedAggregateAttestationAndProof) { 150 root := bytesutil.ToBytes32(att.Message.Aggregate.Data.BeaconBlockRoot) 151 152 s.pendingAttsLock.Lock() 153 defer s.pendingAttsLock.Unlock() 154 _, ok := s.blkRootToPendingAtts[root] 155 if !ok { 156 s.blkRootToPendingAtts[root] = []*ethpb.SignedAggregateAttestationAndProof{att} 157 return 158 } 159 160 // Skip if the attestation from the same aggregator already exists in the pending queue. 161 for _, a := range s.blkRootToPendingAtts[root] { 162 if a.Message.AggregatorIndex == att.Message.AggregatorIndex { 163 return 164 } 165 } 166 167 s.blkRootToPendingAtts[root] = append(s.blkRootToPendingAtts[root], att) 168 } 169 170 // This validates the pending attestations in the queue are still valid. 171 // If not valid, a node will remove it in the queue in place. The validity 172 // check specifies the pending attestation could not fall one epoch behind 173 // of the current slot. 174 func (s *Service) validatePendingAtts(ctx context.Context, slot types.Slot) { 175 ctx, span := trace.StartSpan(ctx, "validatePendingAtts") 176 defer span.End() 177 178 s.pendingAttsLock.Lock() 179 defer s.pendingAttsLock.Unlock() 180 181 for bRoot, atts := range s.blkRootToPendingAtts { 182 for i := len(atts) - 1; i >= 0; i-- { 183 if slot >= atts[i].Message.Aggregate.Data.Slot+params.BeaconConfig().SlotsPerEpoch { 184 // Remove the pending attestation from the list in place. 185 atts = append(atts[:i], atts[i+1:]...) 186 } 187 } 188 s.blkRootToPendingAtts[bRoot] = atts 189 190 // If the pending attestations list of a given block root is empty, 191 // a node will remove the key from the map to avoid dangling keys. 192 if len(s.blkRootToPendingAtts[bRoot]) == 0 { 193 delete(s.blkRootToPendingAtts, bRoot) 194 } 195 } 196 }