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 }