github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/sync/validate_aggregate_proof.go (about)

     1  package sync
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/libp2p/go-libp2p-core/peer"
     8  	pubsub "github.com/libp2p/go-libp2p-pubsub"
     9  	"github.com/pkg/errors"
    10  	types "github.com/prysmaticlabs/eth2-types"
    11  	"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
    12  	"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
    13  	"github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation"
    14  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    15  	"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
    16  	iface "github.com/prysmaticlabs/prysm/beacon-chain/state/interface"
    17  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    18  	"github.com/prysmaticlabs/prysm/shared/bls"
    19  	"github.com/prysmaticlabs/prysm/shared/bytesutil"
    20  	"github.com/prysmaticlabs/prysm/shared/params"
    21  	"github.com/prysmaticlabs/prysm/shared/traceutil"
    22  	"go.opencensus.io/trace"
    23  )
    24  
    25  // validateAggregateAndProof verifies the aggregated signature and the selection proof is valid before forwarding to the
    26  // network and downstream services.
    27  func (s *Service) validateAggregateAndProof(ctx context.Context, pid peer.ID, msg *pubsub.Message) pubsub.ValidationResult {
    28  	if pid == s.cfg.P2P.PeerID() {
    29  		return pubsub.ValidationAccept
    30  	}
    31  
    32  	ctx, span := trace.StartSpan(ctx, "sync.validateAggregateAndProof")
    33  	defer span.End()
    34  
    35  	// To process the following it requires the recent blocks to be present in the database, so we'll skip
    36  	// validating or processing aggregated attestations until fully synced.
    37  	if s.cfg.InitialSync.Syncing() {
    38  		return pubsub.ValidationIgnore
    39  	}
    40  
    41  	raw, err := s.decodePubsubMessage(msg)
    42  	if err != nil {
    43  		log.WithError(err).Debug("Could not decode message")
    44  		traceutil.AnnotateError(span, err)
    45  		return pubsub.ValidationReject
    46  	}
    47  	m, ok := raw.(*ethpb.SignedAggregateAttestationAndProof)
    48  	if !ok {
    49  		return pubsub.ValidationReject
    50  	}
    51  	if m.Message == nil {
    52  		return pubsub.ValidationReject
    53  	}
    54  	if err := helpers.ValidateNilAttestation(m.Message.Aggregate); err != nil {
    55  		return pubsub.ValidationReject
    56  	}
    57  
    58  	// Broadcast the aggregated attestation on a feed to notify other services in the beacon node
    59  	// of a received aggregated attestation.
    60  	s.cfg.AttestationNotifier.OperationFeed().Send(&feed.Event{
    61  		Type: operation.AggregatedAttReceived,
    62  		Data: &operation.AggregatedAttReceivedData{
    63  			Attestation: m.Message,
    64  		},
    65  	})
    66  
    67  	if err := helpers.ValidateSlotTargetEpoch(m.Message.Aggregate.Data); err != nil {
    68  		return pubsub.ValidationReject
    69  	}
    70  
    71  	// Attestation's slot is within ATTESTATION_PROPAGATION_SLOT_RANGE and early attestation
    72  	// processing tolerance.
    73  	if err := helpers.ValidateAttestationTime(m.Message.Aggregate.Data.Slot, s.cfg.Chain.GenesisTime(),
    74  		earlyAttestationProcessingTolerance); err != nil {
    75  		traceutil.AnnotateError(span, err)
    76  		return pubsub.ValidationIgnore
    77  	}
    78  
    79  	// Verify this is the first aggregate received from the aggregator with index and slot.
    80  	if s.hasSeenAggregatorIndexEpoch(m.Message.Aggregate.Data.Target.Epoch, m.Message.AggregatorIndex) {
    81  		return pubsub.ValidationIgnore
    82  	}
    83  	// Check that the block being voted on isn't invalid.
    84  	if s.hasBadBlock(bytesutil.ToBytes32(m.Message.Aggregate.Data.BeaconBlockRoot)) ||
    85  		s.hasBadBlock(bytesutil.ToBytes32(m.Message.Aggregate.Data.Target.Root)) ||
    86  		s.hasBadBlock(bytesutil.ToBytes32(m.Message.Aggregate.Data.Source.Root)) {
    87  		return pubsub.ValidationReject
    88  	}
    89  
    90  	// Verify aggregate attestation has not already been seen via aggregate gossip, within a block, or through the creation locally.
    91  	seen, err := s.cfg.AttPool.HasAggregatedAttestation(m.Message.Aggregate)
    92  	if err != nil {
    93  		traceutil.AnnotateError(span, err)
    94  		return pubsub.ValidationIgnore
    95  	}
    96  	if seen {
    97  		return pubsub.ValidationIgnore
    98  	}
    99  	if !s.validateBlockInAttestation(ctx, m) {
   100  		return pubsub.ValidationIgnore
   101  	}
   102  
   103  	validationRes := s.validateAggregatedAtt(ctx, m)
   104  	if validationRes != pubsub.ValidationAccept {
   105  		return validationRes
   106  	}
   107  
   108  	s.setAggregatorIndexEpochSeen(m.Message.Aggregate.Data.Target.Epoch, m.Message.AggregatorIndex)
   109  
   110  	msg.ValidatorData = m
   111  
   112  	return pubsub.ValidationAccept
   113  }
   114  
   115  func (s *Service) validateAggregatedAtt(ctx context.Context, signed *ethpb.SignedAggregateAttestationAndProof) pubsub.ValidationResult {
   116  	ctx, span := trace.StartSpan(ctx, "sync.validateAggregatedAtt")
   117  	defer span.End()
   118  
   119  	// Verify attestation target root is consistent with the head root.
   120  	// This verification is not in the spec, however we guard against it as it opens us up
   121  	// to weird edge cases during verification. The attestation technically could be used to add value to a block,
   122  	// but it's invalid in the spirit of the protocol. Here we choose safety over profit.
   123  	if err := s.cfg.Chain.VerifyLmdFfgConsistency(ctx, signed.Message.Aggregate); err != nil {
   124  		traceutil.AnnotateError(span, err)
   125  		return pubsub.ValidationReject
   126  	}
   127  
   128  	// Verify current finalized checkpoint is an ancestor of the block defined by the attestation's beacon block root.
   129  	if err := s.cfg.Chain.VerifyFinalizedConsistency(ctx, signed.Message.Aggregate.Data.BeaconBlockRoot); err != nil {
   130  		traceutil.AnnotateError(span, err)
   131  		return pubsub.ValidationReject
   132  	}
   133  
   134  	bs, err := s.cfg.Chain.AttestationPreState(ctx, signed.Message.Aggregate)
   135  	if err != nil {
   136  		traceutil.AnnotateError(span, err)
   137  		return pubsub.ValidationIgnore
   138  	}
   139  
   140  	attSlot := signed.Message.Aggregate.Data.Slot
   141  	// Only advance state if different epoch as the committee can only change on an epoch transition.
   142  	if helpers.SlotToEpoch(attSlot) > helpers.SlotToEpoch(bs.Slot()) {
   143  		startSlot, err := helpers.StartSlot(helpers.SlotToEpoch(attSlot))
   144  		if err != nil {
   145  			return pubsub.ValidationIgnore
   146  		}
   147  		bs, err = state.ProcessSlots(ctx, bs, startSlot)
   148  		if err != nil {
   149  			traceutil.AnnotateError(span, err)
   150  			return pubsub.ValidationIgnore
   151  		}
   152  	}
   153  
   154  	// Verify validator index is within the beacon committee.
   155  	if err := validateIndexInCommittee(ctx, bs, signed.Message.Aggregate, signed.Message.AggregatorIndex); err != nil {
   156  		traceutil.AnnotateError(span, errors.Wrapf(err, "Could not validate index in committee"))
   157  		return pubsub.ValidationReject
   158  	}
   159  
   160  	// Verify selection proof reflects to the right validator.
   161  	selectionSigSet, err := validateSelectionIndex(ctx, bs, signed.Message.Aggregate.Data, signed.Message.AggregatorIndex, signed.Message.SelectionProof)
   162  	if err != nil {
   163  		traceutil.AnnotateError(span, errors.Wrapf(err, "Could not validate selection for validator %d", signed.Message.AggregatorIndex))
   164  		return pubsub.ValidationReject
   165  	}
   166  
   167  	// Verify selection signature, aggregator signature and attestation signature are valid.
   168  	// We use batch verify here to save compute.
   169  	aggregatorSigSet, err := aggSigSet(bs, signed)
   170  	if err != nil {
   171  		traceutil.AnnotateError(span, errors.Wrapf(err, "Could not get aggregator sig set %d", signed.Message.AggregatorIndex))
   172  		return pubsub.ValidationIgnore
   173  	}
   174  	attSigSet, err := blocks.AttestationSignatureSet(ctx, bs, []*ethpb.Attestation{signed.Message.Aggregate})
   175  	if err != nil {
   176  		traceutil.AnnotateError(span, errors.Wrapf(err, "Could not verify aggregator signature %d", signed.Message.AggregatorIndex))
   177  		return pubsub.ValidationIgnore
   178  	}
   179  	set := bls.NewSet()
   180  	set.Join(selectionSigSet).Join(aggregatorSigSet).Join(attSigSet)
   181  	valid, err := set.Verify()
   182  	if err != nil {
   183  		traceutil.AnnotateError(span, errors.Errorf("Could not join signature set"))
   184  		return pubsub.ValidationIgnore
   185  	}
   186  	if !valid {
   187  		traceutil.AnnotateError(span, errors.Errorf("Could not verify selection or aggregator or attestation signature"))
   188  		return pubsub.ValidationReject
   189  	}
   190  
   191  	return pubsub.ValidationAccept
   192  }
   193  
   194  func (s *Service) validateBlockInAttestation(ctx context.Context, satt *ethpb.SignedAggregateAttestationAndProof) bool {
   195  	a := satt.Message
   196  	// Verify the block being voted and the processed state is in DB. The block should have passed validation if it's in the DB.
   197  	blockRoot := bytesutil.ToBytes32(a.Aggregate.Data.BeaconBlockRoot)
   198  	if !s.hasBlockAndState(ctx, blockRoot) {
   199  		// A node doesn't have the block, it'll request from peer while saving the pending attestation to a queue.
   200  		s.savePendingAtt(satt)
   201  		return false
   202  	}
   203  	return true
   204  }
   205  
   206  // Returns true if the node has received aggregate for the aggregator with index and target epoch.
   207  func (s *Service) hasSeenAggregatorIndexEpoch(epoch types.Epoch, aggregatorIndex types.ValidatorIndex) bool {
   208  	s.seenAggregatedAttestationLock.RLock()
   209  	defer s.seenAggregatedAttestationLock.RUnlock()
   210  	b := append(bytesutil.Bytes32(uint64(epoch)), bytesutil.Bytes32(uint64(aggregatorIndex))...)
   211  	_, seen := s.seenAggregatedAttestationCache.Get(string(b))
   212  	return seen
   213  }
   214  
   215  // Set aggregate's aggregator index target epoch as seen.
   216  func (s *Service) setAggregatorIndexEpochSeen(epoch types.Epoch, aggregatorIndex types.ValidatorIndex) {
   217  	s.seenAggregatedAttestationLock.Lock()
   218  	defer s.seenAggregatedAttestationLock.Unlock()
   219  	b := append(bytesutil.Bytes32(uint64(epoch)), bytesutil.Bytes32(uint64(aggregatorIndex))...)
   220  	s.seenAggregatedAttestationCache.Add(string(b), true)
   221  }
   222  
   223  // This validates the aggregator's index in state is within the beacon committee.
   224  func validateIndexInCommittee(ctx context.Context, bs iface.ReadOnlyBeaconState, a *ethpb.Attestation, validatorIndex types.ValidatorIndex) error {
   225  	ctx, span := trace.StartSpan(ctx, "sync.validateIndexInCommittee")
   226  	defer span.End()
   227  
   228  	committee, err := helpers.BeaconCommitteeFromState(bs, a.Data.Slot, a.Data.CommitteeIndex)
   229  	if err != nil {
   230  		return err
   231  	}
   232  	var withinCommittee bool
   233  	for _, i := range committee {
   234  		if validatorIndex == i {
   235  			withinCommittee = true
   236  			break
   237  		}
   238  	}
   239  	if !withinCommittee {
   240  		return fmt.Errorf("validator index %d is not within the committee: %v",
   241  			validatorIndex, committee)
   242  	}
   243  	return nil
   244  }
   245  
   246  // This validates selection proof by validating it's from the correct validator index of the slot.
   247  // It does not verify the selection proof, it returns the signature set of selection proof which can be used for batch verify.
   248  func validateSelectionIndex(
   249  	ctx context.Context,
   250  	bs iface.ReadOnlyBeaconState,
   251  	data *ethpb.AttestationData,
   252  	validatorIndex types.ValidatorIndex,
   253  	proof []byte,
   254  ) (*bls.SignatureSet, error) {
   255  	_, span := trace.StartSpan(ctx, "sync.validateSelectionIndex")
   256  	defer span.End()
   257  
   258  	committee, err := helpers.BeaconCommitteeFromState(bs, data.Slot, data.CommitteeIndex)
   259  	if err != nil {
   260  		return nil, err
   261  	}
   262  	aggregator, err := helpers.IsAggregator(uint64(len(committee)), proof)
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  	if !aggregator {
   267  		return nil, fmt.Errorf("validator is not an aggregator for slot %d", data.Slot)
   268  	}
   269  
   270  	domain := params.BeaconConfig().DomainSelectionProof
   271  	epoch := helpers.SlotToEpoch(data.Slot)
   272  
   273  	v, err := bs.ValidatorAtIndex(validatorIndex)
   274  	if err != nil {
   275  		return nil, err
   276  	}
   277  	publicKey, err := bls.PublicKeyFromBytes(v.PublicKey)
   278  	if err != nil {
   279  		return nil, err
   280  	}
   281  
   282  	d, err := helpers.Domain(bs.Fork(), epoch, domain, bs.GenesisValidatorRoot())
   283  	if err != nil {
   284  		return nil, err
   285  	}
   286  	sszUint := types.SSZUint64(data.Slot)
   287  	root, err := helpers.ComputeSigningRoot(&sszUint, d)
   288  	if err != nil {
   289  		return nil, err
   290  	}
   291  	return &bls.SignatureSet{
   292  		Signatures: [][]byte{proof},
   293  		PublicKeys: []bls.PublicKey{publicKey},
   294  		Messages:   [][32]byte{root},
   295  	}, nil
   296  }
   297  
   298  // This returns aggregator signature set which can be used to batch verify.
   299  func aggSigSet(s iface.ReadOnlyBeaconState, a *ethpb.SignedAggregateAttestationAndProof) (*bls.SignatureSet, error) {
   300  	v, err := s.ValidatorAtIndex(a.Message.AggregatorIndex)
   301  	if err != nil {
   302  		return nil, err
   303  	}
   304  	publicKey, err := bls.PublicKeyFromBytes(v.PublicKey)
   305  	if err != nil {
   306  		return nil, err
   307  	}
   308  
   309  	epoch := helpers.SlotToEpoch(a.Message.Aggregate.Data.Slot)
   310  	d, err := helpers.Domain(s.Fork(), epoch, params.BeaconConfig().DomainAggregateAndProof, s.GenesisValidatorRoot())
   311  	if err != nil {
   312  		return nil, err
   313  	}
   314  	root, err := helpers.ComputeSigningRoot(a.Message, d)
   315  	if err != nil {
   316  		return nil, err
   317  	}
   318  	return &bls.SignatureSet{
   319  		Signatures: [][]byte{a.Signature},
   320  		PublicKeys: []bls.PublicKey{publicKey},
   321  		Messages:   [][32]byte{root},
   322  	}, nil
   323  }