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  }