Skip to content

Commit

Permalink
feat: VersionedAttestations (#447)
Browse files Browse the repository at this point in the history
* feat: update go modules and dependencies

This commit updates the go modules and dependencies, including:
- Replacing github.com/probe-lab/hermes with github.com/ethpandaops/hermes
- Upgrading github.com/libp2p/go-libp2p to v0.38.1
- Upgrading go.opentelemetry.io/otel and related modules to v1.33.0
- Updating various other dependencies to their latest versions

chore: update go.sum dependencies

This commit updates the dependencies in go.sum to their latest
versions. This includes updates to libraries such as aws-sdk-go-v2,
go-openapi, pion, and others. These updates may include bug fixes,
performance improvements, and new features.

chore: update go.sum dependencies to latest versions

This commit updates dependencies in go.sum to their latest
versions to ensure compatibility and access to the newest
features and security patches.

chore: update go.sum dependencies

This commit updates the dependencies in go.sum to their latest versions.
This ensures that the project is using the most up-to-date and
compatible versions of its dependencies.

feat: add electra support to beacon block processing

This commit adds support for the Electra fork version in beacon block
processing. It includes changes to handle Electra blocks in gossipsub
beacon block handling, event data extraction, and validator block
filtering. This ensures that the system can correctly process and
interpret Electra-formatted beacon blocks.

* chore(.goreleaser.yaml): disable darwin builds for amd64 and arm64

The darwin builds are disabled because cross-compilation is not working.

* feat: add support for electra versioned attestation
refactor: use local beacon package instead of github one

* fix: update go-eth2-client dependency to v0.24.1

The go-eth2-client dependency was updated to the latest version
v0.24.1-0.20250219090147-b41ce952806c to address potential bugs
and incorporate the newest features.

* feat(sentry): refactor attestation event processing for improved data handling

This commit refactors the attestation event processing logic to improve data handling and error management. The main changes include:

- Modified the `NewEventsAttestation` function to accept `spec.VersionedAttestation` instead of `beacon.VersionedAttestation`.
- Added error handling to the `NewEventsAttestation` function to return an error if the event is nil or if it fails to get attestation data.
- Added `attestationData` field to the `EventsAttestation` struct to store the attestation data.
- Modified the `getAdditionalData` function to use the `attestationData` field instead of calling methods on the `event` field.
- Modified the `getAdditionalData` function to get the aggregation bits from the event and append the validator index if the attestation is unaggregated.
- Updated the `OnAttestation` function in `sentry.go` to use the new `NewEventsAttestation` function and handle errors.

These changes improve the data handling and error management of the attestation event processing logic.

* feat: bump beacon dependency to v0.48.0
fix(events_attestation.go): use attestationData instead of event.Phase0.Data to get slot and index

* refactor(events_attestation.go): remove attestationData from struct

The attestationData field was removed from the EventsAttestation struct.
Instead of storing the data directly, the AttestationData method is now
used to retrieve it when needed. This change reduces the struct size and
avoids storing redundant data.
  • Loading branch information
samcm authored Mar 5, 2025
1 parent c6604c6 commit 3eddacb
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 36 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ replace github.com/probe-lab/hermes => github.com/ethpandaops/hermes v0.0.3-0.20

require (
github.com/IBM/sarama v1.45.0
github.com/attestantio/go-eth2-client v0.24.0
github.com/attestantio/go-eth2-client v0.24.1-0.20250219090147-b41ce952806c
github.com/avast/retry-go/v4 v4.5.1
github.com/beevik/ntp v1.4.3
github.com/cenkalti/backoff/v5 v5.0.2
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9
github.com/creasty/defaults v1.8.0
github.com/ethereum/go-ethereum v1.15.0
github.com/ethpandaops/beacon v0.47.0
github.com/ethpandaops/beacon v0.48.0
github.com/ethpandaops/ethcore v0.0.0-20240422023000-2a5727b18756
github.com/ethpandaops/ethwallclock v0.3.0
github.com/ferranbt/fastssz v0.1.4
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/attestantio/go-eth2-client v0.24.0 h1:lGVbcnhlBwRglt1Zs56JOCgXVyLWKFZOmZN8jKhE7Ws=
github.com/attestantio/go-eth2-client v0.24.0/go.mod h1:/KTLN3WuH1xrJL7ZZrpBoWM1xCCihnFbzequD5L+83o=
github.com/attestantio/go-eth2-client v0.24.1-0.20250219090147-b41ce952806c h1:i1p3AyXvNfUffEKAnnnoJVfDBYLRPLa/BQ8q5Mf+kY0=
github.com/attestantio/go-eth2-client v0.24.1-0.20250219090147-b41ce952806c/go.mod h1:/KTLN3WuH1xrJL7ZZrpBoWM1xCCihnFbzequD5L+83o=
github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o=
github.com/avast/retry-go/v4 v4.5.1/go.mod h1:/sipNsvNB3RRuT5iNcb6h73nw3IBmXJ/H3XrCQYSOpc=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
Expand Down Expand Up @@ -225,8 +225,8 @@ github.com/ethereum/go-ethereum v1.15.0 h1:LLb2jCPsbJZcB4INw+E/MgzUX5wlR6SdwXcv0
github.com/ethereum/go-ethereum v1.15.0/go.mod h1:4q+4t48P2C03sjqGvTXix5lEOplf5dz4CTosbjt5tGs=
github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
github.com/ethpandaops/beacon v0.47.0 h1:/b45kaumLvKkdGUgfnalVlFONE02e1/woyqEsOPRpck=
github.com/ethpandaops/beacon v0.47.0/go.mod h1:Ht6AyKOImp6NDZANaH/seHauwaD8xF1SEDIvrBcxONc=
github.com/ethpandaops/beacon v0.48.0 h1:rnlTrDyaaIE2qS/7Xtde5qSv8Os39Vgj25gwKJGdNAU=
github.com/ethpandaops/beacon v0.48.0/go.mod h1:NklxAyo+7cmTORZb/sOYcU8sS9bpTgGCfGZr2ZUjdSM=
github.com/ethpandaops/ethcore v0.0.0-20240422023000-2a5727b18756 h1:8JWjrRfP14m0oxOk03m11n/xgdY5ceyUf/ZxYdOs5gE=
github.com/ethpandaops/ethcore v0.0.0-20240422023000-2a5727b18756/go.mod h1:ZvKqL6CKxiraefdXPHeJurV2pDD/f2HF2uklDVdrry8=
github.com/ethpandaops/ethwallclock v0.3.0 h1:xF5fwtBf+bHFHZKBnwiPFEuelW3sMM7SD3ZNFq1lJY4=
Expand Down
127 changes: 99 additions & 28 deletions pkg/sentry/event/beacon/eth/v1/events_attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"time"

"github.com/attestantio/go-eth2-client/spec"
"github.com/attestantio/go-eth2-client/spec/phase0"
xatuethv1 "github.com/ethpandaops/xatu/pkg/proto/eth/v1"
"github.com/ethpandaops/xatu/pkg/proto/xatu"
Expand All @@ -22,14 +23,18 @@ type EventsAttestation struct {

now time.Time

event *phase0.Attestation
event *spec.VersionedAttestation
beacon *ethereum.BeaconNode
duplicateCache *ttlcache.Cache[string, time.Time]
clientMeta *xatu.ClientMeta
id uuid.UUID
}

func NewEventsAttestation(log logrus.FieldLogger, event *phase0.Attestation, now time.Time, beacon *ethereum.BeaconNode, duplicateCache *ttlcache.Cache[string, time.Time], clientMeta *xatu.ClientMeta) *EventsAttestation {
func NewEventsAttestation(log logrus.FieldLogger, event *spec.VersionedAttestation, now time.Time, beacon *ethereum.BeaconNode, duplicateCache *ttlcache.Cache[string, time.Time], clientMeta *xatu.ClientMeta) (*EventsAttestation, error) {
if event == nil {
return nil, fmt.Errorf("event is nil")
}

return &EventsAttestation{
log: log.WithField("event", "BEACON_API_ETH_V1_EVENTS_ATTESTATION_V2"),
now: now,
Expand All @@ -38,10 +43,76 @@ func NewEventsAttestation(log logrus.FieldLogger, event *phase0.Attestation, now
duplicateCache: duplicateCache,
clientMeta: clientMeta,
id: uuid.New(),
}, nil
}

func (e *EventsAttestation) AttestationData() (*phase0.AttestationData, error) {
data, err := e.event.Data()
if err != nil {
return nil, fmt.Errorf("failed to get attestation data: %w", err)
}

return data, nil
}

func (e *EventsAttestation) getData() (*xatuethv1.AttestationV2, error) {
switch e.event.Version {
case spec.DataVersionPhase0:
attestation := e.event.Phase0
if attestation == nil {
return nil, fmt.Errorf("phase0 attestation is nil")
}

return &xatuethv1.AttestationV2{
AggregationBits: xatuethv1.BytesToString(attestation.AggregationBits),
Data: &xatuethv1.AttestationDataV2{
Slot: &wrapperspb.UInt64Value{Value: uint64(attestation.Data.Slot)},
Index: &wrapperspb.UInt64Value{Value: uint64(attestation.Data.Index)},
BeaconBlockRoot: xatuethv1.RootAsString(attestation.Data.BeaconBlockRoot),
Source: &xatuethv1.CheckpointV2{
Epoch: &wrapperspb.UInt64Value{Value: uint64(attestation.Data.Source.Epoch)},
Root: xatuethv1.RootAsString(attestation.Data.Source.Root),
},
Target: &xatuethv1.CheckpointV2{
Epoch: &wrapperspb.UInt64Value{Value: uint64(attestation.Data.Target.Epoch)},
Root: xatuethv1.RootAsString(attestation.Data.Target.Root),
},
},
Signature: xatuethv1.TrimmedString(fmt.Sprintf("%#x", attestation.Signature)),
}, nil
case spec.DataVersionElectra:
attestation := e.event.Electra
if attestation == nil {
return nil, fmt.Errorf("electra attestation is nil")
}

return &xatuethv1.AttestationV2{
AggregationBits: xatuethv1.BytesToString(attestation.AggregationBits),
Data: &xatuethv1.AttestationDataV2{
Slot: &wrapperspb.UInt64Value{Value: uint64(attestation.Data.Slot)},
Index: &wrapperspb.UInt64Value{Value: uint64(attestation.Data.Index)},
BeaconBlockRoot: xatuethv1.RootAsString(attestation.Data.BeaconBlockRoot),
Source: &xatuethv1.CheckpointV2{
Epoch: &wrapperspb.UInt64Value{Value: uint64(attestation.Data.Source.Epoch)},
Root: xatuethv1.RootAsString(attestation.Data.Source.Root),
},
Target: &xatuethv1.CheckpointV2{
Epoch: &wrapperspb.UInt64Value{Value: uint64(attestation.Data.Target.Epoch)},
Root: xatuethv1.RootAsString(attestation.Data.Target.Root),
},
},
}, nil
default:
return nil, fmt.Errorf("unsupported attestation version: %s", e.event.Version)
}
}

func (e *EventsAttestation) Decorate(ctx context.Context) (*xatu.DecoratedEvent, error) {
data, err := e.getData()
if err != nil {
return nil, err
}

decoratedEvent := &xatu.DecoratedEvent{
Event: &xatu.Event{
Name: xatu.Event_BEACON_API_ETH_V1_EVENTS_ATTESTATION_V2,
Expand All @@ -52,23 +123,7 @@ func (e *EventsAttestation) Decorate(ctx context.Context) (*xatu.DecoratedEvent,
Client: e.clientMeta,
},
Data: &xatu.DecoratedEvent_EthV1EventsAttestationV2{
EthV1EventsAttestationV2: &xatuethv1.AttestationV2{
AggregationBits: xatuethv1.BytesToString(e.event.AggregationBits),
Data: &xatuethv1.AttestationDataV2{
Slot: &wrapperspb.UInt64Value{Value: uint64(e.event.Data.Slot)},
Index: &wrapperspb.UInt64Value{Value: uint64(e.event.Data.Index)},
BeaconBlockRoot: xatuethv1.RootAsString(e.event.Data.BeaconBlockRoot),
Source: &xatuethv1.CheckpointV2{
Epoch: &wrapperspb.UInt64Value{Value: uint64(e.event.Data.Source.Epoch)},
Root: xatuethv1.RootAsString(e.event.Data.Source.Root),
},
Target: &xatuethv1.CheckpointV2{
Epoch: &wrapperspb.UInt64Value{Value: uint64(e.event.Data.Target.Epoch)},
Root: xatuethv1.RootAsString(e.event.Data.Target.Root),
},
},
Signature: xatuethv1.TrimmedString(fmt.Sprintf("%#x", e.event.Signature)),
},
EthV1EventsAttestationV2: data,
},
}

Expand Down Expand Up @@ -99,7 +154,6 @@ func (e *EventsAttestation) ShouldIgnore(ctx context.Context) (bool, error) {
e.log.WithFields(logrus.Fields{
"hash": hash,
"time_since_first_item": time.Since(item.Value()),
"slot": e.event.Data.Slot,
}).Debug("Duplicate attestation event received")

return true, nil
Expand All @@ -111,8 +165,15 @@ func (e *EventsAttestation) ShouldIgnore(ctx context.Context) (bool, error) {
func (e *EventsAttestation) getAdditionalData(_ context.Context) (*xatu.ClientMeta_AdditionalEthV1EventsAttestationV2Data, error) {
extra := &xatu.ClientMeta_AdditionalEthV1EventsAttestationV2Data{}

attestionSlot := e.beacon.Metadata().Wallclock().Slots().FromNumber(uint64(e.event.Data.Slot))
epoch := e.beacon.Metadata().Wallclock().Epochs().FromSlot(uint64(e.event.Data.Slot))
attestationData, err := e.AttestationData()
if err != nil {
return nil, err
}

slot := attestationData.Slot

attestionSlot := e.beacon.Metadata().Wallclock().Slots().FromNumber(uint64(slot))
epoch := e.beacon.Metadata().Wallclock().Epochs().FromSlot(uint64(slot))

extra.Slot = &xatu.SlotV2{
Number: &wrapperspb.UInt64Value{Value: attestionSlot.Number()},
Expand All @@ -131,17 +192,21 @@ func (e *EventsAttestation) getAdditionalData(_ context.Context) (*xatu.ClientMe
},
}

target := attestationData.Target

// Build out the target section
targetEpoch := e.beacon.Metadata().Wallclock().Epochs().FromNumber(uint64(e.event.Data.Target.Epoch))
targetEpoch := e.beacon.Metadata().Wallclock().Epochs().FromNumber(uint64(target.Epoch))
extra.Target = &xatu.ClientMeta_AdditionalEthV1AttestationTargetV2Data{
Epoch: &xatu.EpochV2{
Number: &wrapperspb.UInt64Value{Value: targetEpoch.Number()},
StartDateTime: timestamppb.New(targetEpoch.TimeWindow().Start()),
},
}

source := attestationData.Source

// Build out the source section
sourceEpoch := e.beacon.Metadata().Wallclock().Epochs().FromNumber(uint64(e.event.Data.Source.Epoch))
sourceEpoch := e.beacon.Metadata().Wallclock().Epochs().FromNumber(uint64(source.Epoch))
extra.Source = &xatu.ClientMeta_AdditionalEthV1AttestationSourceV2Data{
Epoch: &xatu.EpochV2{
Number: &wrapperspb.UInt64Value{Value: sourceEpoch.Number()},
Expand All @@ -150,14 +215,20 @@ func (e *EventsAttestation) getAdditionalData(_ context.Context) (*xatu.ClientMe
}

// If the attestation is unaggreated, we can append the validator position within the committee
if e.event.AggregationBits.Count() == 1 {
aggregationBits, err := e.event.AggregationBits()
if err != nil {
return nil, err
}

// Append the validator index if the attestation is unaggreated
if aggregationBits.Count() == 1 {
//nolint:gosec // not concerned in reality
position := uint64(e.event.AggregationBits.BitIndices()[0])
position := uint64(aggregationBits.BitIndices()[0])

validatorIndex, err := e.beacon.Duties().GetValidatorIndex(
phase0.Epoch(epoch.Number()),
e.event.Data.Slot,
e.event.Data.Index,
attestationData.Slot,
attestationData.Index,
position,
)
if err == nil {
Expand Down
8 changes: 6 additions & 2 deletions pkg/sentry/sentry.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"time"

eth2v1 "github.com/attestantio/go-eth2-client/api/v1"
"github.com/attestantio/go-eth2-client/spec"
"github.com/attestantio/go-eth2-client/spec/altair"
"github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/beevik/ntp"
Expand Down Expand Up @@ -278,15 +279,18 @@ func (s *Sentry) Start(ctx context.Context) error {
s.log.Fatal("Unable to determine Ethereum network. Provide an override network name via ethereum.overrideNetworkName")
}

s.beacon.Node().OnAttestation(ctx, func(ctx context.Context, attestation *phase0.Attestation) error {
s.beacon.Node().OnAttestation(ctx, func(ctx context.Context, ev *spec.VersionedAttestation) error {
now := time.Now().Add(s.clockDrift)

meta, err := s.createNewClientMeta(ctx)
if err != nil {
return err
}

event := v1.NewEventsAttestation(s.log, attestation, now, s.beacon, s.duplicateCache.BeaconETHV1EventsAttestation, meta)
event, err := v1.NewEventsAttestation(s.log, ev, now, s.beacon, s.duplicateCache.BeaconETHV1EventsAttestation, meta)
if err != nil {
return err
}

ignore, err := event.ShouldIgnore(ctx)
if err != nil {
Expand Down

0 comments on commit 3eddacb

Please sign in to comment.