github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/operations/attestations/prepare_forkchoice.go (about)

     1  package attestations
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"time"
     8  
     9  	"github.com/prysmaticlabs/prysm/shared/copyutil"
    10  
    11  	"github.com/prysmaticlabs/go-bitfield"
    12  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    13  	attaggregation "github.com/prysmaticlabs/prysm/shared/aggregation/attestations"
    14  	"github.com/prysmaticlabs/prysm/shared/hashutil"
    15  	"github.com/prysmaticlabs/prysm/shared/slotutil"
    16  	"go.opencensus.io/trace"
    17  )
    18  
    19  // Prepare attestations for fork choice three times per slot.
    20  var prepareForkChoiceAttsPeriod = slotutil.DivideSlotBy(3 /* times-per-slot */)
    21  
    22  // This prepares fork choice attestations by running batchForkChoiceAtts
    23  // every prepareForkChoiceAttsPeriod.
    24  func (s *Service) prepareForkChoiceAtts() {
    25  	ticker := time.NewTicker(prepareForkChoiceAttsPeriod)
    26  	defer ticker.Stop()
    27  	for {
    28  		select {
    29  		case <-ticker.C:
    30  			if err := s.batchForkChoiceAtts(s.ctx); err != nil {
    31  				log.WithError(err).Error("Could not prepare attestations for fork choice")
    32  			}
    33  		case <-s.ctx.Done():
    34  			log.Debug("Context closed, exiting routine")
    35  			return
    36  		}
    37  	}
    38  }
    39  
    40  // This gets the attestations from the unaggregated, aggregated and block
    41  // pool. Then finds the common data, aggregate and batch them for fork choice.
    42  // The resulting attestations are saved in the fork choice pool.
    43  func (s *Service) batchForkChoiceAtts(ctx context.Context) error {
    44  	ctx, span := trace.StartSpan(ctx, "Operations.attestations.batchForkChoiceAtts")
    45  	defer span.End()
    46  
    47  	if err := s.cfg.Pool.AggregateUnaggregatedAttestations(ctx); err != nil {
    48  		return err
    49  	}
    50  	atts := append(s.cfg.Pool.AggregatedAttestations(), s.cfg.Pool.BlockAttestations()...)
    51  	atts = append(atts, s.cfg.Pool.ForkchoiceAttestations()...)
    52  
    53  	attsByDataRoot := make(map[[32]byte][]*ethpb.Attestation, len(atts))
    54  
    55  	// Consolidate attestations by aggregating them by similar data root.
    56  	for _, att := range atts {
    57  		seen, err := s.seen(att)
    58  		if err != nil {
    59  			return err
    60  		}
    61  		if seen {
    62  			continue
    63  		}
    64  
    65  		attDataRoot, err := att.Data.HashTreeRoot()
    66  		if err != nil {
    67  			return err
    68  		}
    69  		attsByDataRoot[attDataRoot] = append(attsByDataRoot[attDataRoot], att)
    70  	}
    71  
    72  	for _, atts := range attsByDataRoot {
    73  		if err := s.aggregateAndSaveForkChoiceAtts(atts); err != nil {
    74  			return err
    75  		}
    76  	}
    77  
    78  	for _, a := range s.cfg.Pool.BlockAttestations() {
    79  		if err := s.cfg.Pool.DeleteBlockAttestation(a); err != nil {
    80  			return err
    81  		}
    82  	}
    83  
    84  	return nil
    85  }
    86  
    87  // This aggregates a list of attestations using the aggregation algorithm defined in AggregateAttestations
    88  // and saves the attestations for fork choice.
    89  func (s *Service) aggregateAndSaveForkChoiceAtts(atts []*ethpb.Attestation) error {
    90  	clonedAtts := make([]*ethpb.Attestation, len(atts))
    91  	for i, a := range atts {
    92  		clonedAtts[i] = copyutil.CopyAttestation(a)
    93  	}
    94  	aggregatedAtts, err := attaggregation.Aggregate(clonedAtts)
    95  	if err != nil {
    96  		return err
    97  	}
    98  
    99  	return s.cfg.Pool.SaveForkchoiceAttestations(aggregatedAtts)
   100  }
   101  
   102  // This checks if the attestation has previously been aggregated for fork choice
   103  // return true if yes, false if no.
   104  func (s *Service) seen(att *ethpb.Attestation) (bool, error) {
   105  	attRoot, err := hashutil.HashProto(att.Data)
   106  	if err != nil {
   107  		return false, err
   108  	}
   109  	incomingBits := att.AggregationBits
   110  	savedBits, ok := s.forkChoiceProcessedRoots.Get(attRoot)
   111  	if ok {
   112  		savedBitlist, ok := savedBits.(bitfield.Bitlist)
   113  		if !ok {
   114  			return false, errors.New("not a bit field")
   115  		}
   116  		if savedBitlist.Len() == incomingBits.Len() {
   117  			// Returns true if the node has seen all the bits in the new bit field of the incoming attestation.
   118  			if bytes.Equal(savedBitlist, incomingBits) {
   119  				return true, nil
   120  			}
   121  			if c, err := savedBitlist.Contains(incomingBits); err != nil {
   122  				return false, err
   123  			} else if c {
   124  				return true, nil
   125  			}
   126  			var err error
   127  			// Update the bit fields by Or'ing them with the new ones.
   128  			incomingBits, err = incomingBits.Or(savedBitlist)
   129  			if err != nil {
   130  				return false, err
   131  			}
   132  		}
   133  	}
   134  
   135  	s.forkChoiceProcessedRoots.Add(attRoot, incomingBits)
   136  	return false, nil
   137  }