mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 05:47:59 -05:00
Compare commits
2 Commits
v5.1.0
...
move-sig-v
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d60c2a125 | ||
|
|
e011f05403 |
@@ -30,6 +30,10 @@ func (s *Service) receiveAttestations(ctx context.Context, indexedAttsChan chan
|
||||
for {
|
||||
select {
|
||||
case att := <-indexedAttsChan:
|
||||
if att == nil || att.SignatureValidationSet == nil {
|
||||
log.Error("Received nil attestation or attestation with nil signature validation set")
|
||||
continue
|
||||
}
|
||||
if !validateAttestationIntegrity(att) {
|
||||
continue
|
||||
}
|
||||
@@ -38,6 +42,16 @@ func (s *Service) receiveAttestations(ctx context.Context, indexedAttsChan chan
|
||||
log.WithError(err).Error("Could not get hash tree root of attestation")
|
||||
continue
|
||||
}
|
||||
verified, err := att.SignatureValidationSet.Verify()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Could not verify attestation signature")
|
||||
continue
|
||||
}
|
||||
if !verified {
|
||||
log.Error("Attestation signature did not verify")
|
||||
continue
|
||||
}
|
||||
// Verify the attestation signature using the signature batch.
|
||||
attWrapper := &slashertypes.IndexedAttestationWrapper{
|
||||
IndexedAttestation: att.IndexedAtt,
|
||||
DataRoot: dataRoot,
|
||||
|
||||
@@ -2,6 +2,7 @@ package types
|
||||
|
||||
import (
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
@@ -31,6 +32,7 @@ func (c ChunkKind) String() string {
|
||||
// which doesn't work well with interface types.
|
||||
type WrappedIndexedAtt struct {
|
||||
ethpb.IndexedAtt
|
||||
SignatureValidationSet *bls.SignatureBatch
|
||||
}
|
||||
|
||||
// IndexedAttestationWrapper contains an indexed attestation with its
|
||||
|
||||
@@ -132,7 +132,7 @@ func (s *Service) processAttestations(ctx context.Context, attestations []ethpb.
|
||||
continue
|
||||
}
|
||||
|
||||
valid, err := s.validateUnaggregatedAttWithState(ctx, aggregate, preState)
|
||||
valid, _, err := s.validateUnaggregatedAttWithState(ctx, aggregate, preState)
|
||||
if err != nil {
|
||||
log.WithError(err).Debug("Pending unaggregated attestation failed validation")
|
||||
continue
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v5/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
|
||||
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing"
|
||||
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
||||
@@ -113,19 +114,17 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(ctx context.Context, p
|
||||
committeeIndex = data.CommitteeIndex
|
||||
}
|
||||
|
||||
if !features.Get().EnableSlasher {
|
||||
// Verify this the first attestation received for the participating validator for the slot.
|
||||
if s.hasSeenCommitteeIndicesSlot(data.Slot, data.CommitteeIndex, att.GetAggregationBits()) {
|
||||
return pubsub.ValidationIgnore, nil
|
||||
}
|
||||
// Verify this the first attestation received for the participating validator for the slot.
|
||||
if s.hasSeenCommitteeIndicesSlot(data.Slot, data.CommitteeIndex, att.GetAggregationBits()) {
|
||||
return pubsub.ValidationIgnore, nil
|
||||
}
|
||||
|
||||
// Reject an attestation if it references an invalid block.
|
||||
if s.hasBadBlock(bytesutil.ToBytes32(data.BeaconBlockRoot)) ||
|
||||
s.hasBadBlock(bytesutil.ToBytes32(data.Target.Root)) ||
|
||||
s.hasBadBlock(bytesutil.ToBytes32(data.Source.Root)) {
|
||||
attBadBlockCount.Inc()
|
||||
return pubsub.ValidationReject, errors.New("attestation data references bad block root")
|
||||
}
|
||||
// Reject an attestation if it references an invalid block.
|
||||
if s.hasBadBlock(bytesutil.ToBytes32(data.BeaconBlockRoot)) ||
|
||||
s.hasBadBlock(bytesutil.ToBytes32(data.Target.Root)) ||
|
||||
s.hasBadBlock(bytesutil.ToBytes32(data.Source.Root)) {
|
||||
attBadBlockCount.Inc()
|
||||
return pubsub.ValidationReject, errors.New("attestation data references bad block root")
|
||||
}
|
||||
|
||||
// Verify the block being voted and the processed state is in beaconDB and the block has passed validation if it's in the beaconDB.
|
||||
@@ -171,7 +170,7 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(ctx context.Context, p
|
||||
return validationRes, err
|
||||
}
|
||||
|
||||
validationRes, err = s.validateUnaggregatedAttWithState(ctx, att, preState)
|
||||
validationRes, signatureValidationSet, err := s.validateUnaggregatedAttWithState(ctx, att, preState)
|
||||
if validationRes != pubsub.ValidationAccept {
|
||||
return validationRes, err
|
||||
}
|
||||
@@ -201,7 +200,9 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(ctx context.Context, p
|
||||
tracing.AnnotateError(span, err)
|
||||
return
|
||||
}
|
||||
s.cfg.slasherAttestationsFeed.Send(&types.WrappedIndexedAtt{IndexedAtt: indexedAtt})
|
||||
s.cfg.slasherAttestationsFeed.Send(&types.WrappedIndexedAtt{
|
||||
IndexedAtt: indexedAtt, SignatureValidationSet: signatureValidationSet,
|
||||
})
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -217,7 +218,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,43 +236,63 @@ 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)
|
||||
}
|
||||
return valCount, pubsub.ValidationAccept, nil
|
||||
|
||||
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 ci, valCount, pubsub.ValidationAccept, nil
|
||||
}
|
||||
|
||||
// This validates beacon unaggregated attestation using the given state, the validation consists of bitfield length and count consistency
|
||||
// and signature verification.
|
||||
func (s *Service) validateUnaggregatedAttWithState(ctx context.Context, a eth.Att, bs state.ReadOnlyBeaconState) (pubsub.ValidationResult, error) {
|
||||
func (s *Service) validateUnaggregatedAttWithState(ctx context.Context, a eth.Att, bs state.ReadOnlyBeaconState) (pubsub.ValidationResult, *bls.SignatureBatch, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "sync.validateUnaggregatedAttWithState")
|
||||
defer span.End()
|
||||
|
||||
committee, result, err := s.validateBitLength(ctx, bs, a.GetData().Slot, a.GetData().CommitteeIndex, a.GetAggregationBits())
|
||||
if result != pubsub.ValidationAccept {
|
||||
return result, err
|
||||
return result, nil, err
|
||||
}
|
||||
|
||||
// Attestation must be unaggregated and the bit index must exist in the range of committee indices.
|
||||
// Note: The Ethereum Beacon chain spec suggests (len(get_attesting_indices(state, attestation.data, attestation.aggregation_bits)) == 1)
|
||||
// however this validation can be achieved without use of get_attesting_indices which is an O(n) lookup.
|
||||
if a.GetAggregationBits().Count() != 1 || a.GetAggregationBits().BitIndices()[0] >= len(committee) {
|
||||
return pubsub.ValidationReject, errors.New("attestation bitfield is invalid")
|
||||
return pubsub.ValidationReject, nil, errors.New("attestation bitfield is invalid")
|
||||
}
|
||||
|
||||
set, err := blocks.AttestationSignatureBatch(ctx, bs, []eth.Att{a})
|
||||
if err != nil {
|
||||
tracing.AnnotateError(span, err)
|
||||
attBadSignatureBatchCount.Inc()
|
||||
return pubsub.ValidationReject, err
|
||||
return pubsub.ValidationReject, nil, err
|
||||
}
|
||||
return s.validateWithBatchVerifier(ctx, "attestation", set)
|
||||
res, err := s.validateWithBatchVerifier(ctx, "attestation", set)
|
||||
return res, set, err
|
||||
}
|
||||
|
||||
func (s *Service) validateBitLength(
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user