github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/blockchain/receive_attestation.go (about) 1 package blockchain 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "time" 8 9 "github.com/pkg/errors" 10 "github.com/prysmaticlabs/prysm/beacon-chain/core/feed" 11 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 12 iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface" 13 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 14 "github.com/prysmaticlabs/prysm/shared/bytesutil" 15 "github.com/prysmaticlabs/prysm/shared/params" 16 "github.com/prysmaticlabs/prysm/shared/slotutil" 17 "github.com/sirupsen/logrus" 18 "go.opencensus.io/trace" 19 ) 20 21 // AttestationReceiver interface defines the methods of chain service receive and processing new attestations. 22 type AttestationReceiver interface { 23 ReceiveAttestationNoPubsub(ctx context.Context, att *ethpb.Attestation) error 24 AttestationPreState(ctx context.Context, att *ethpb.Attestation) (iface.BeaconState, error) 25 VerifyLmdFfgConsistency(ctx context.Context, att *ethpb.Attestation) error 26 VerifyFinalizedConsistency(ctx context.Context, root []byte) error 27 } 28 29 // ReceiveAttestationNoPubsub is a function that defines the operations that are performed on 30 // attestation that is received from regular sync. The operations consist of: 31 // 1. Validate attestation, update validator's latest vote 32 // 2. Apply fork choice to the processed attestation 33 // 3. Save latest head info 34 func (s *Service) ReceiveAttestationNoPubsub(ctx context.Context, att *ethpb.Attestation) error { 35 ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.ReceiveAttestationNoPubsub") 36 defer span.End() 37 38 if err := s.onAttestation(ctx, att); err != nil { 39 return errors.Wrap(err, "could not process attestation") 40 } 41 42 return nil 43 } 44 45 // AttestationPreState returns the pre state of attestation. 46 func (s *Service) AttestationPreState(ctx context.Context, att *ethpb.Attestation) (iface.BeaconState, error) { 47 ss, err := helpers.StartSlot(att.Data.Target.Epoch) 48 if err != nil { 49 return nil, err 50 } 51 if err := helpers.ValidateSlotClock(ss, uint64(s.genesisTime.Unix())); err != nil { 52 return nil, err 53 } 54 return s.getAttPreState(ctx, att.Data.Target) 55 } 56 57 // VerifyLmdFfgConsistency verifies that attestation's LMD and FFG votes are consistency to each other. 58 func (s *Service) VerifyLmdFfgConsistency(ctx context.Context, a *ethpb.Attestation) error { 59 targetSlot, err := helpers.StartSlot(a.Data.Target.Epoch) 60 if err != nil { 61 return err 62 } 63 r, err := s.ancestor(ctx, a.Data.BeaconBlockRoot, targetSlot) 64 if err != nil { 65 return err 66 } 67 if !bytes.Equal(a.Data.Target.Root, r) { 68 return errors.New("FFG and LMD votes are not consistent") 69 } 70 return nil 71 } 72 73 // VerifyFinalizedConsistency verifies input root is consistent with finalized store. 74 // When the input root is not be consistent with finalized store then we know it is not 75 // on the finalized check point that leads to current canonical chain and should be rejected accordingly. 76 func (s *Service) VerifyFinalizedConsistency(ctx context.Context, root []byte) error { 77 // A canonical root implies the root to has an ancestor that aligns with finalized check point. 78 // In this case, we could exit early to save on additional computation. 79 if s.cfg.ForkChoiceStore.IsCanonical(bytesutil.ToBytes32(root)) { 80 return nil 81 } 82 83 f := s.FinalizedCheckpt() 84 ss, err := helpers.StartSlot(f.Epoch) 85 if err != nil { 86 return err 87 } 88 r, err := s.ancestor(ctx, root, ss) 89 if err != nil { 90 return err 91 } 92 if !bytes.Equal(f.Root, r) { 93 return errors.New("Root and finalized store are not consistent") 94 } 95 96 return nil 97 } 98 99 // This routine processes fork choice attestations from the pool to account for validator votes and fork choice. 100 func (s *Service) processAttestationsRoutine(subscribedToStateEvents chan<- struct{}) { 101 // Wait for state to be initialized. 102 stateChannel := make(chan *feed.Event, 1) 103 stateSub := s.cfg.StateNotifier.StateFeed().Subscribe(stateChannel) 104 subscribedToStateEvents <- struct{}{} 105 <-stateChannel 106 stateSub.Unsubscribe() 107 108 if s.genesisTime.IsZero() { 109 log.Warn("ProcessAttestations routine waiting for genesis time") 110 for s.genesisTime.IsZero() { 111 time.Sleep(1 * time.Second) 112 } 113 log.Warn("Genesis time received, now available to process attestations") 114 } 115 116 st := slotutil.NewSlotTicker(s.genesisTime, params.BeaconConfig().SecondsPerSlot) 117 for { 118 select { 119 case <-s.ctx.Done(): 120 return 121 case <-st.C(): 122 // Continue when there's no fork choice attestation, there's nothing to process and update head. 123 // This covers the condition when the node is still initial syncing to the head of the chain. 124 if s.cfg.AttPool.ForkchoiceAttestationCount() == 0 { 125 continue 126 } 127 s.processAttestations(s.ctx) 128 if err := s.updateHead(s.ctx, s.getJustifiedBalances()); err != nil { 129 log.Warnf("Resolving fork due to new attestation: %v", err) 130 } 131 } 132 } 133 } 134 135 // This processes fork choice attestations from the pool to account for validator votes and fork choice. 136 func (s *Service) processAttestations(ctx context.Context) { 137 atts := s.cfg.AttPool.ForkchoiceAttestations() 138 for _, a := range atts { 139 // Based on the spec, don't process the attestation until the subsequent slot. 140 // This delays consideration in the fork choice until their slot is in the past. 141 // https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/fork-choice.md#validate_on_attestation 142 nextSlot := a.Data.Slot + 1 143 if err := helpers.VerifySlotTime(uint64(s.genesisTime.Unix()), nextSlot, params.BeaconNetworkConfig().MaximumGossipClockDisparity); err != nil { 144 continue 145 } 146 147 hasState := s.cfg.BeaconDB.HasStateSummary(ctx, bytesutil.ToBytes32(a.Data.BeaconBlockRoot)) 148 hasBlock := s.hasBlock(ctx, bytesutil.ToBytes32(a.Data.BeaconBlockRoot)) 149 if !(hasState && hasBlock) { 150 continue 151 } 152 153 if err := s.cfg.AttPool.DeleteForkchoiceAttestation(a); err != nil { 154 log.WithError(err).Error("Could not delete fork choice attestation in pool") 155 } 156 157 if !helpers.VerifyCheckpointEpoch(a.Data.Target, s.genesisTime) { 158 continue 159 } 160 161 if err := s.ReceiveAttestationNoPubsub(ctx, a); err != nil { 162 log.WithFields(logrus.Fields{ 163 "slot": a.Data.Slot, 164 "committeeIndex": a.Data.CommitteeIndex, 165 "beaconBlockRoot": fmt.Sprintf("%#x", bytesutil.Trunc(a.Data.BeaconBlockRoot)), 166 "targetRoot": fmt.Sprintf("%#x", bytesutil.Trunc(a.Data.Target.Root)), 167 "aggregationCount": a.AggregationBits.Count(), 168 }).WithError(err).Warn("Could not process attestation for fork choice") 169 } 170 } 171 }