From e011f0540360d6e87837311240aa2f0ccf9c4069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Kapka?= Date: Thu, 8 Aug 2024 18:32:19 +0200 Subject: [PATCH] Electra committe validation for aggregate and proof (#14317) * Electra committe validation for aggregate and proof * review * update comments --- beacon-chain/sync/validate_aggregate_proof.go | 8 +++-- .../sync/validate_aggregate_proof_test.go | 25 ++++++++++++---- .../sync/validate_beacon_attestation.go | 29 +++++++++++++++---- .../validate_beacon_attestation_electra.go | 3 ++ 4 files changed, 53 insertions(+), 12 deletions(-) diff --git a/beacon-chain/sync/validate_aggregate_proof.go b/beacon-chain/sync/validate_aggregate_proof.go index 8c753b6eb7d8..e5f4d0436a9f 100644 --- a/beacon-chain/sync/validate_aggregate_proof.go +++ b/beacon-chain/sync/validate_aggregate_proof.go @@ -266,16 +266,20 @@ func (s *Service) setAggregatorIndexEpochSeen(epoch primitives.Epoch, aggregator // - [REJECT] The aggregate attestation has participants -- that is, len(get_attesting_indices(state, aggregate.data, aggregate.aggregation_bits)) >= 1. // - [REJECT] The aggregator's validator index is within the committee -- // i.e. `aggregate_and_proof.aggregator_index in get_beacon_committee(state, aggregate.data.slot, aggregate.data.index)`. +// +// Post-Electra: +// - [REJECT] len(committee_indices) == 1, where committee_indices = get_committee_indices(aggregate). +// - [REJECT] aggregate.data.index == 0 func (s *Service) validateIndexInCommittee(ctx context.Context, bs state.ReadOnlyBeaconState, a ethpb.Att, validatorIndex primitives.ValidatorIndex) (pubsub.ValidationResult, error) { ctx, span := trace.StartSpan(ctx, "sync.validateIndexInCommittee") defer span.End() - _, result, err := s.validateCommitteeIndex(ctx, a, bs) + committeeIndex, _, result, err := s.validateCommitteeIndex(ctx, a, bs) if result != pubsub.ValidationAccept { return result, err } - committee, result, err := s.validateBitLength(ctx, bs, a.GetData().Slot, a.GetData().CommitteeIndex, a.GetAggregationBits()) + committee, result, err := s.validateBitLength(ctx, bs, a.GetData().Slot, committeeIndex, a.GetAggregationBits()) if result != pubsub.ValidationAccept { return result, err } diff --git a/beacon-chain/sync/validate_aggregate_proof_test.go b/beacon-chain/sync/validate_aggregate_proof_test.go index a8e5539aed13..f1dd12292ddf 100644 --- a/beacon-chain/sync/validate_aggregate_proof_test.go +++ b/beacon-chain/sync/validate_aggregate_proof_test.go @@ -44,9 +44,7 @@ func TestVerifyIndexInCommittee_CanVerify(t *testing.T) { bf := bitfield.NewBitlist(validators / uint64(params.BeaconConfig().SlotsPerEpoch)) bf.SetBitAt(0, true) - att := ðpb.Attestation{Data: ðpb.AttestationData{ - Target: ðpb.Checkpoint{Epoch: 0}}, - AggregationBits: bf} + att := ðpb.Attestation{Data: ðpb.AttestationData{}, AggregationBits: bf} committee, err := helpers.BeaconCommitteeFromState(context.Background(), s, att.Data.Slot, att.Data.CommitteeIndex) assert.NoError(t, err) @@ -71,8 +69,7 @@ func TestVerifyIndexInCommittee_ExistsInBeaconCommittee(t *testing.T) { s, _ := util.DeterministicGenesisState(t, validators) require.NoError(t, s.SetSlot(params.BeaconConfig().SlotsPerEpoch)) - att := ðpb.Attestation{Data: ðpb.AttestationData{ - Target: ðpb.Checkpoint{Epoch: 0}}} + att := ðpb.Attestation{Data: ðpb.AttestationData{}} committee, err := helpers.BeaconCommitteeFromState(context.Background(), s, att.Data.Slot, att.Data.CommitteeIndex) require.NoError(t, err) @@ -107,6 +104,24 @@ func TestVerifyIndexInCommittee_ExistsInBeaconCommittee(t *testing.T) { assert.Equal(t, pubsub.ValidationReject, result) } +func TestVerifyIndexInCommittee_Electra(t *testing.T) { + ctx := context.Background() + s, _ := util.DeterministicGenesisStateElectra(t, 64) + service := &Service{} + cb := primitives.NewAttestationCommitteeBits() + cb.SetBitAt(0, true) + att := ðpb.AttestationElectra{Data: ðpb.AttestationData{}, CommitteeBits: cb} + committee, err := helpers.BeaconCommitteeFromState(context.Background(), s, att.Data.Slot, att.Data.CommitteeIndex) + require.NoError(t, err) + bl := bitfield.NewBitlist(uint64(len(committee))) + bl.SetBitAt(0, true) + att.AggregationBits = bl + + result, err := service.validateIndexInCommittee(ctx, s, att, committee[0]) + require.NoError(t, err) + assert.Equal(t, pubsub.ValidationAccept, result) +} + func TestVerifySelection_NotAnAggregator(t *testing.T) { ctx := context.Background() params.SetupTestConfigCleanup(t) diff --git a/beacon-chain/sync/validate_beacon_attestation.go b/beacon-chain/sync/validate_beacon_attestation.go index 860b0d8a2ba7..79a86f83bf60 100644 --- a/beacon-chain/sync/validate_beacon_attestation.go +++ b/beacon-chain/sync/validate_beacon_attestation.go @@ -217,7 +217,7 @@ func (s *Service) validateUnaggregatedAttTopic(ctx context.Context, a eth.Att, b ctx, span := trace.StartSpan(ctx, "sync.validateUnaggregatedAttTopic") defer span.End() - valCount, result, err := s.validateCommitteeIndex(ctx, a, bs) + _, valCount, result, err := s.validateCommitteeIndex(ctx, a, bs) if result != pubsub.ValidationAccept { return result, err } @@ -235,16 +235,35 @@ func (s *Service) validateUnaggregatedAttTopic(ctx context.Context, a eth.Att, b return pubsub.ValidationAccept, nil } -func (s *Service) validateCommitteeIndex(ctx context.Context, a eth.Att, bs state.ReadOnlyBeaconState) (uint64, pubsub.ValidationResult, error) { +func (s *Service) validateCommitteeIndex( + ctx context.Context, + a eth.Att, + bs state.ReadOnlyBeaconState, +) (primitives.CommitteeIndex, uint64, pubsub.ValidationResult, error) { valCount, err := helpers.ActiveValidatorCount(ctx, bs, slots.ToEpoch(a.GetData().Slot)) if err != nil { - return 0, pubsub.ValidationIgnore, err + return 0, 0, pubsub.ValidationIgnore, err } count := helpers.SlotCommitteeCount(valCount) if uint64(a.GetData().CommitteeIndex) > count { - return 0, pubsub.ValidationReject, errors.Errorf("committee index %d > %d", a.GetData().CommitteeIndex, count) + return 0, 0, pubsub.ValidationReject, errors.Errorf("committee index %d > %d", a.GetData().CommitteeIndex, count) + } + + var ci primitives.CommitteeIndex + if a.Version() >= version.Electra { + dataCi := a.GetData().CommitteeIndex + if dataCi != 0 { + return 0, 0, pubsub.ValidationReject, fmt.Errorf("committee index must be 0 but was %d", dataCi) + } + indices := helpers.CommitteeIndices(a.CommitteeBitsVal()) + if len(indices) != 1 { + return 0, 0, pubsub.ValidationReject, fmt.Errorf("exactly 1 committee index must be set but %d were set", len(indices)) + } + ci = indices[0] + } else { + ci = a.GetData().CommitteeIndex } - return valCount, pubsub.ValidationAccept, nil + return ci, valCount, pubsub.ValidationAccept, nil } // This validates beacon unaggregated attestation using the given state, the validation consists of bitfield length and count consistency diff --git a/beacon-chain/sync/validate_beacon_attestation_electra.go b/beacon-chain/sync/validate_beacon_attestation_electra.go index 3ed3fe9c1899..e0693f0f3d91 100644 --- a/beacon-chain/sync/validate_beacon_attestation_electra.go +++ b/beacon-chain/sync/validate_beacon_attestation_electra.go @@ -11,6 +11,9 @@ import ( "go.opencensus.io/trace" ) +// validateCommitteeIndexElectra implements the following checks from the spec: +// - [REJECT] len(committee_indices) == 1, where committee_indices = get_committee_indices(attestation). +// - [REJECT] attestation.data.index == 0 func validateCommitteeIndexElectra(ctx context.Context, a *ethpb.AttestationElectra) (primitives.CommitteeIndex, pubsub.ValidationResult, error) { _, span := trace.StartSpan(ctx, "sync.validateCommitteeIndexElectra") defer span.End()