From 7d0e607f96b8ae774ff34eebe9ed8c5ab5e7e3ad Mon Sep 17 00:00:00 2001 From: Chris Berry Date: Mon, 27 Jan 2025 10:01:36 +0000 Subject: [PATCH 1/4] Update to use electra version of go-eth2-client --- .gitignore | 6 ++ CHANGELOG.md | 3 + cmd/attester/inclusion/output.go | 3 +- cmd/attester/inclusion/process.go | 35 +++++++++--- cmd/block/analyze/process.go | 95 +++++++++++++++++++------------ cmd/epoch/summary/process.go | 30 ++++++---- cmd/validator/summary/process.go | 20 +++++-- go.mod | 2 +- go.sum | 2 + testing/mock/eth2client.go | 4 +- util/attestations.go | 38 +++++++++---- 11 files changed, 163 insertions(+), 75 deletions(-) diff --git a/.gitignore b/.gitignore index 616fdea..02023e9 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,12 @@ coverage.html # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 .glide/ +# Intellij +.idea/ + +# Makefile +Makefile + # Vim *.sw? diff --git a/CHANGELOG.md b/CHANGELOG.md index 9278187..914d84f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +electra: + - update to handle versioned attestations from go-eth2-client electra branch + 1.36.2: - avoid crash when signing and verifing signatures using keys rather than accounts diff --git a/cmd/attester/inclusion/output.go b/cmd/attester/inclusion/output.go index c81aa76..37dc902 100644 --- a/cmd/attester/inclusion/output.go +++ b/cmd/attester/inclusion/output.go @@ -19,6 +19,7 @@ import ( "strconv" "strings" + "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/pkg/errors" ) @@ -27,7 +28,7 @@ type dataOut struct { debug bool quiet bool verbose bool - attestation *phase0.Attestation + attestation *spec.VersionedAttestation slot phase0.Slot attestationIndex uint64 inclusionDelay phase0.Slot diff --git a/cmd/attester/inclusion/process.go b/cmd/attester/inclusion/process.go index 9701334..b7b5813 100644 --- a/cmd/attester/inclusion/process.go +++ b/cmd/attester/inclusion/process.go @@ -22,6 +22,7 @@ import ( eth2client "github.com/attestantio/go-eth2-client" "github.com/attestantio/go-eth2-client/api" apiv1 "github.com/attestantio/go-eth2-client/api/v1" + "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/pkg/errors" standardchaintime "github.com/wealdtech/ethdo/services/chaintime/standard" @@ -93,9 +94,17 @@ func process(ctx context.Context, data *dataIn) (*dataOut, error) { return nil, errors.Wrap(err, "failed to obtain block attestations") } for i, attestation := range attestations { - if attestation.Data.Slot == duty.Slot && - attestation.Data.Index == duty.CommitteeIndex && - attestation.AggregationBits.BitAt(duty.ValidatorCommitteeIndex) { + attestationData, err := attestation.Data() + if err != nil { + return nil, errors.Wrap(err, "failed to obtain attestation data") + } + aggregationBits, err := attestation.AggregationBits() + if err != nil { + return nil, errors.Wrap(err, "failed to obtain aggregation bits") + } + if attestationData.Slot == duty.Slot && + attestationData.Index == duty.CommitteeIndex && + aggregationBits.BitAt(duty.ValidatorCommitteeIndex) { headCorrect := false targetCorrect := false if data.verbose { @@ -128,8 +137,12 @@ func process(ctx context.Context, data *dataIn) (*dataOut, error) { return results, nil } -func calcHeadCorrect(ctx context.Context, data *dataIn, attestation *phase0.Attestation) (bool, error) { - slot := attestation.Data.Slot +func calcHeadCorrect(ctx context.Context, data *dataIn, attestation *spec.VersionedAttestation) (bool, error) { + attestationData, err := attestation.Data() + if err != nil { + return false, errors.Wrap(err, "failed to obtain attestation data") + } + slot := attestationData.Slot for { response, err := data.eth2Client.(eth2client.BeaconBlockHeadersProvider).BeaconBlockHeader(ctx, &api.BeaconBlockHeaderOpts{ Block: fmt.Sprintf("%d", slot), @@ -149,13 +162,17 @@ func calcHeadCorrect(ctx context.Context, data *dataIn, attestation *phase0.Atte slot-- continue } - return bytes.Equal(response.Data.Root[:], attestation.Data.BeaconBlockRoot[:]), nil + return bytes.Equal(response.Data.Root[:], attestationData.BeaconBlockRoot[:]), nil } } -func calcTargetCorrect(ctx context.Context, data *dataIn, attestation *phase0.Attestation) (bool, error) { +func calcTargetCorrect(ctx context.Context, data *dataIn, attestation *spec.VersionedAttestation) (bool, error) { + attestationData, err := attestation.Data() + if err != nil { + return false, errors.Wrap(err, "failed to obtain attestation data") + } // Start with first slot of the target epoch. - slot := data.chainTime.FirstSlotOfEpoch(attestation.Data.Target.Epoch) + slot := data.chainTime.FirstSlotOfEpoch(attestationData.Target.Epoch) for { response, err := data.eth2Client.(eth2client.BeaconBlockHeadersProvider).BeaconBlockHeader(ctx, &api.BeaconBlockHeaderOpts{ Block: fmt.Sprintf("%d", slot), @@ -175,7 +192,7 @@ func calcTargetCorrect(ctx context.Context, data *dataIn, attestation *phase0.At slot-- continue } - return bytes.Equal(response.Data.Root[:], attestation.Data.Target.Root[:]), nil + return bytes.Equal(response.Data.Root[:], attestationData.Target.Root[:]), nil } } diff --git a/cmd/block/analyze/process.go b/cmd/block/analyze/process.go index 66b3ef2..7e7524a 100644 --- a/cmd/block/analyze/process.go +++ b/cmd/block/analyze/process.go @@ -63,8 +63,12 @@ func (c *command) process(ctx context.Context) error { // Calculate how many parents we need to fetch. minSlot := slot for _, attestation := range attestations { - if attestation.Data.Slot < minSlot { - minSlot = attestation.Data.Slot + attestData, err := attestation.Data() + if err != nil { + return errors.Wrap(err, "failed to obtain attestation data") + } + if attestData.Slot < minSlot { + minSlot = attestData.Slot } } if c.debug { @@ -103,10 +107,14 @@ func (c *command) analyzeAttestations(ctx context.Context, block *spec.Versioned if c.debug { fmt.Printf("Processing attestation %d\n", i) } + attestData, err := attestation.Data() + if err != nil { + return errors.Wrap(err, "failed to obtain attestation data") + } analysis := &attestationAnalysis{ - Head: attestation.Data.BeaconBlockRoot, - Target: attestation.Data.Target.Root, - Distance: int(slot - attestation.Data.Slot), + Head: attestData.BeaconBlockRoot, + Target: attestData.Target.Root, + Distance: int(slot - attestData.Slot), } root, err := attestation.HashTreeRoot() @@ -116,45 +124,47 @@ func (c *command) analyzeAttestations(ctx context.Context, block *spec.Versioned if info, exists := c.priorAttestations[fmt.Sprintf("%#x", root)]; exists { analysis.Duplicate = info } else { - data := attestation.Data - _, exists := blockVotes[data.Slot] - if !exists { - blockVotes[data.Slot] = make(map[phase0.CommitteeIndex]bitfield.Bitlist) + aggregationBits, err := attestation.AggregationBits() + if err != nil { + return err } - _, exists = blockVotes[data.Slot][data.Index] + _, exists := blockVotes[attestData.Slot] if !exists { - blockVotes[data.Slot][data.Index] = bitfield.NewBitlist(attestation.AggregationBits.Len()) + blockVotes[attestData.Slot] = make(map[phase0.CommitteeIndex]bitfield.Bitlist) + } + _, exists = blockVotes[attestData.Slot][attestData.Index] + if !exists { + blockVotes[attestData.Slot][attestData.Index] = bitfield.NewBitlist(aggregationBits.Len()) } // Count new votes. - analysis.PossibleVotes = int(attestation.AggregationBits.Len()) - for j := range attestation.AggregationBits.Len() { - if attestation.AggregationBits.BitAt(j) { + analysis.PossibleVotes = int(aggregationBits.Len()) + for j := range aggregationBits.Len() { + if aggregationBits.BitAt(j) { analysis.Votes++ - if blockVotes[data.Slot][data.Index].BitAt(j) { + if blockVotes[attestData.Slot][attestData.Index].BitAt(j) { // Already attested to in this block; skip. continue } - if c.votes[data.Slot][data.Index].BitAt(j) { + if c.votes[attestData.Slot][attestData.Index].BitAt(j) { // Already attested to in a previous block; skip. continue } analysis.NewVotes++ - blockVotes[data.Slot][data.Index].SetBitAt(j, true) + blockVotes[attestData.Slot][attestData.Index].SetBitAt(j, true) } } // Calculate head correct. - var err error analysis.HeadCorrect, err = c.calcHeadCorrect(ctx, attestation) if err != nil { return err } // Calculate head timely. - analysis.HeadTimely = analysis.HeadCorrect && attestation.Data.Slot == slot-1 + analysis.HeadTimely = analysis.HeadCorrect && attestData.Slot == slot-1 // Calculate source timely. - analysis.SourceTimely = attestation.Data.Slot >= slot-5 + analysis.SourceTimely = attestData.Slot >= slot-5 // Calculate target correct. analysis.TargetCorrect, err = c.calcTargetCorrect(ctx, attestation) @@ -164,7 +174,7 @@ func (c *command) analyzeAttestations(ctx context.Context, block *spec.Versioned // Calculate target timely. if block.Version < spec.DataVersionDeneb { - analysis.TargetTimely = attestation.Data.Slot >= slot-32 + analysis.TargetTimely = attestData.Slot >= slot-32 } else { analysis.TargetTimely = true } @@ -260,17 +270,24 @@ func (c *command) processParentBlock(_ context.Context, block *spec.VersionedSig Index: i, } - data := attestation.Data + data, err := attestation.Data() + if err != nil { + return err + } _, exists := c.votes[data.Slot] if !exists { c.votes[data.Slot] = make(map[phase0.CommitteeIndex]bitfield.Bitlist) } _, exists = c.votes[data.Slot][data.Index] - if !exists { - c.votes[data.Slot][data.Index] = bitfield.NewBitlist(attestation.AggregationBits.Len()) + aggregationBits, err := attestation.AggregationBits() + if err != nil { + return err } - for j := range attestation.AggregationBits.Len() { - if attestation.AggregationBits.BitAt(j) { + if !exists { + c.votes[data.Slot][data.Index] = bitfield.NewBitlist(aggregationBits.Len()) + } + for j := range aggregationBits.Len() { + if aggregationBits.BitAt(j) { c.votes[data.Slot][data.Index].SetBitAt(j, true) } } @@ -385,8 +402,12 @@ func (c *command) setup(ctx context.Context) error { return nil } -func (c *command) calcHeadCorrect(ctx context.Context, attestation *phase0.Attestation) (bool, error) { - slot := attestation.Data.Slot +func (c *command) calcHeadCorrect(ctx context.Context, attestation *spec.VersionedAttestation) (bool, error) { + attestData, err := attestation.Data() + if err != nil { + return false, errors.Wrap(err, "failed to obtain attestation data") + } + slot := attestData.Slot root, exists := c.headRoots[slot] if !exists { for { @@ -413,20 +434,24 @@ func (c *command) calcHeadCorrect(ctx context.Context, attestation *phase0.Attes slot-- continue } - c.headRoots[attestation.Data.Slot] = response.Data.Root + c.headRoots[attestData.Slot] = response.Data.Root root = response.Data.Root break } } - return bytes.Equal(root[:], attestation.Data.BeaconBlockRoot[:]), nil + return bytes.Equal(root[:], attestData.BeaconBlockRoot[:]), nil } -func (c *command) calcTargetCorrect(ctx context.Context, attestation *phase0.Attestation) (bool, error) { - root, exists := c.targetRoots[attestation.Data.Slot] +func (c *command) calcTargetCorrect(ctx context.Context, attestation *spec.VersionedAttestation) (bool, error) { + attestData, err := attestation.Data() + if err != nil { + return false, errors.Wrap(err, "failed to obtain attestation data") + } + root, exists := c.targetRoots[attestData.Slot] if !exists { // Start with first slot of the target epoch. - slot := c.chainTime.FirstSlotOfEpoch(attestation.Data.Target.Epoch) + slot := c.chainTime.FirstSlotOfEpoch(attestData.Target.Epoch) for { response, err := c.blockHeadersProvider.BeaconBlockHeader(ctx, &api.BeaconBlockHeaderOpts{ Block: fmt.Sprintf("%d", slot), @@ -450,12 +475,12 @@ func (c *command) calcTargetCorrect(ctx context.Context, attestation *phase0.Att slot-- continue } - c.targetRoots[attestation.Data.Slot] = response.Data.Root + c.targetRoots[attestData.Slot] = response.Data.Root root = response.Data.Root break } } - return bytes.Equal(root[:], attestation.Data.Target.Root[:]), nil + return bytes.Equal(root[:], attestData.Target.Root[:]), nil } func (c *command) analyzeSyncCommittees(_ context.Context, block *spec.VersionedSignedBeaconBlock) error { diff --git a/cmd/epoch/summary/process.go b/cmd/epoch/summary/process.go index 93759f1..aecc49a 100644 --- a/cmd/epoch/summary/process.go +++ b/cmd/epoch/summary/process.go @@ -239,17 +239,21 @@ func (c *command) processSlots(ctx context.Context, return nil, nil, nil, nil, nil, nil, nil, err } for _, attestation := range attestations { - if attestation.Data.Slot < c.chainTime.FirstSlotOfEpoch(c.summary.Epoch) || attestation.Data.Slot >= c.chainTime.FirstSlotOfEpoch(c.summary.Epoch+1) { + attestationData, err := attestation.Data() + if err != nil { + return nil, nil, nil, nil, nil, nil, nil, errors.Wrap(err, "failed to obtain attestation data") + } + if attestationData.Slot < c.chainTime.FirstSlotOfEpoch(c.summary.Epoch) || attestationData.Slot >= c.chainTime.FirstSlotOfEpoch(c.summary.Epoch+1) { // Outside of this epoch's range. continue } - slotCommittees, exists := allCommittees[attestation.Data.Slot] + slotCommittees, exists := allCommittees[attestationData.Slot] if !exists { response, err := c.beaconCommitteesProvider.BeaconCommittees(ctx, &api.BeaconCommitteesOpts{ - State: fmt.Sprintf("%d", attestation.Data.Slot), + State: fmt.Sprintf("%d", attestationData.Slot), }) if err != nil { - return nil, nil, nil, nil, nil, nil, nil, errors.Wrap(err, fmt.Sprintf("failed to obtain committees for slot %d", attestation.Data.Slot)) + return nil, nil, nil, nil, nil, nil, nil, errors.Wrap(err, fmt.Sprintf("failed to obtain committees for slot %d", attestationData.Slot)) } for _, beaconCommittee := range response.Data { if _, exists := allCommittees[beaconCommittee.Slot]; !exists { @@ -275,11 +279,11 @@ func (c *command) processSlots(ctx context.Context, } } } - slotCommittees = allCommittees[attestation.Data.Slot] + slotCommittees = allCommittees[attestationData.Slot] } - committee := slotCommittees[attestation.Data.Index] + committee := slotCommittees[attestationData.Index] - inclusionDistance := slot - attestation.Data.Slot + inclusionDistance := slot - attestationData.Slot head, err := util.AttestationHead(ctx, headersCache, attestation) if err != nil { @@ -298,8 +302,12 @@ func (c *command) processSlots(ctx context.Context, return nil, nil, nil, nil, nil, nil, nil, err } - for i := range attestation.AggregationBits.Len() { - if attestation.AggregationBits.BitAt(i) { + aggregationBits, err := attestation.AggregationBits() + if err != nil { + return nil, nil, nil, nil, nil, nil, nil, errors.Wrap(err, "failed to obtain aggregation bits") + } + for i := range aggregationBits.Len() { + if aggregationBits.BitAt(i) { validatorIndex := committee[int(i)] if len(c.validators) > 0 { if _, exists := c.validators[validatorIndex]; !exists { @@ -310,9 +318,9 @@ func (c *command) processSlots(ctx context.Context, // Only set the information from the first attestation we find for this validator. if participations[validatorIndex].InclusionSlot == 0 { - participations[validatorIndex].HeadVote = &attestation.Data.BeaconBlockRoot + participations[validatorIndex].HeadVote = &attestationData.BeaconBlockRoot participations[validatorIndex].Head = &head - participations[validatorIndex].TargetVote = &attestation.Data.Target.Root + participations[validatorIndex].TargetVote = &attestationData.Target.Root participations[validatorIndex].Target = &target participations[validatorIndex].InclusionSlot = slot } diff --git a/cmd/validator/summary/process.go b/cmd/validator/summary/process.go index e9166d0..c74c6f4 100644 --- a/cmd/validator/summary/process.go +++ b/cmd/validator/summary/process.go @@ -243,16 +243,24 @@ func (c *command) processAttesterDutiesSlot(ctx context.Context, return err } for _, attestation := range attestations { - if _, exists := dutiesBySlot[attestation.Data.Slot]; !exists { + attestationData, err := attestation.Data() + if err != nil { + return errors.Wrap(err, "failed to obtain attestation data") + } + if _, exists := dutiesBySlot[attestationData.Slot]; !exists { // We do not have any attestations for this slot. continue } - if _, exists := dutiesBySlot[attestation.Data.Slot][attestation.Data.Index]; !exists { + if _, exists := dutiesBySlot[attestationData.Slot][attestationData.Index]; !exists { // We do not have any attestations for this committee. continue } - for _, duty := range dutiesBySlot[attestation.Data.Slot][attestation.Data.Index] { - if attestation.AggregationBits.BitAt(duty.ValidatorCommitteeIndex) { + for _, duty := range dutiesBySlot[attestationData.Slot][attestationData.Index] { + aggregationBits, err := attestation.AggregationBits() + if err != nil { + return errors.Wrap(err, "failed to obtain aggregation bits") + } + if aggregationBits.BitAt(duty.ValidatorCommitteeIndex) { // Found it. if _, exists := votes[duty.ValidatorIndex]; exists { // Duplicate; ignore. @@ -261,13 +269,13 @@ func (c *command) processAttesterDutiesSlot(ctx context.Context, votes[duty.ValidatorIndex] = struct{}{} // Update the metrics for the attestation. - index := int(attestation.Data.Slot - c.chainTime.FirstSlotOfEpoch(c.summary.Epoch)) + index := int(attestationData.Slot - c.chainTime.FirstSlotOfEpoch(c.summary.Epoch)) c.summary.Slots[index].Attestations.Included++ inclusionDelay := slot - duty.Slot fault := &validatorFault{ Validator: duty.ValidatorIndex, - AttestationData: attestation.Data, + AttestationData: attestationData, InclusionDistance: int(inclusionDelay), } diff --git a/go.mod b/go.mod index 02a7c5e..c38bb07 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22.7 toolchain go1.23.2 require ( - github.com/attestantio/go-eth2-client v0.22.0 + github.com/attestantio/go-eth2-client v0.23.1-0.20250127091537-251e60f042d4 github.com/ferranbt/fastssz v0.1.4 github.com/gofrs/uuid v4.4.0+incompatible github.com/google/uuid v1.6.0 diff --git a/go.sum b/go.sum index 6f57784..7836a12 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/attestantio/go-eth2-client v0.22.0 h1:KmF9kPNNWWGfE7l1BP7pXps4EOXgKnYeFGR0/WbyFhY= github.com/attestantio/go-eth2-client v0.22.0/go.mod h1:d7ZPNrMX8jLfIgML5u7QZxFo2AukLM+5m08iMaLdqb8= +github.com/attestantio/go-eth2-client v0.23.1-0.20250127091537-251e60f042d4 h1:ePstQwO3RoDX2am93bvtUNLsMtxlikx9kPJDdzr9sk8= +github.com/attestantio/go-eth2-client v0.23.1-0.20250127091537-251e60f042d4/go.mod h1:vy5jU/uDZ2+RcVzq5BfnG+bQ3/6uu9DGwCrGsPtjJ1A= github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= diff --git a/testing/mock/eth2client.go b/testing/mock/eth2client.go index 7896d41..fa257fa 100644 --- a/testing/mock/eth2client.go +++ b/testing/mock/eth2client.go @@ -119,7 +119,7 @@ func NewAttestationSubmitter() eth2client.AttestationsSubmitter { } // SubmitAttestations is a mock. -func (m *AttestationsSubmitter) SubmitAttestations(_ context.Context, _ []*phase0.Attestation) error { +func (m *AttestationsSubmitter) SubmitAttestations(_ context.Context, _ *api.SubmitAttestationsOpts) error { return nil } @@ -145,7 +145,7 @@ func NewAggregateAttestationsSubmitter() eth2client.AggregateAttestationsSubmitt } // SubmitAggregateAttestations is a mock. -func (m *AggregateAttestationsSubmitter) SubmitAggregateAttestations(_ context.Context, _ []*phase0.SignedAggregateAndProof) error { +func (m *AggregateAttestationsSubmitter) SubmitAggregateAttestations(_ context.Context, _ *api.SubmitAggregateAttestationsOpts) error { return nil } diff --git a/util/attestations.go b/util/attestations.go index 0a812a3..7624c23 100644 --- a/util/attestations.go +++ b/util/attestations.go @@ -17,19 +17,25 @@ import ( "bytes" "context" + "github.com/attestantio/go-eth2-client/spec" "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/pkg/errors" "github.com/wealdtech/ethdo/services/chaintime" ) // AttestationHead returns the head for which the attestation should have voted. func AttestationHead(ctx context.Context, headersCache *BeaconBlockHeaderCache, - attestation *phase0.Attestation, + attestation *spec.VersionedAttestation, ) ( phase0.Root, error, ) { - slot := attestation.Data.Slot + attestationData, err := attestation.Data() + if err != nil { + return phase0.Root{}, errors.Wrap(err, "failed to obtain attestation data") + } + slot := attestationData.Slot for { header, err := headersCache.Fetch(ctx, slot) if err != nil { @@ -53,12 +59,16 @@ func AttestationHead(ctx context.Context, // AttestationHeadCorrect returns true if the given attestation had the correct head. func AttestationHeadCorrect(ctx context.Context, headersCache *BeaconBlockHeaderCache, - attestation *phase0.Attestation, + attestation *spec.VersionedAttestation, ) ( bool, error, ) { - slot := attestation.Data.Slot + attestationData, err := attestation.Data() + if err != nil { + return false, errors.Wrap(err, "failed to obtain attestation data") + } + slot := attestationData.Slot for { header, err := headersCache.Fetch(ctx, slot) if err != nil { @@ -74,7 +84,7 @@ func AttestationHeadCorrect(ctx context.Context, slot-- continue } - return bytes.Equal(header.Root[:], attestation.Data.BeaconBlockRoot[:]), nil + return bytes.Equal(header.Root[:], attestationData.BeaconBlockRoot[:]), nil } } @@ -82,13 +92,17 @@ func AttestationHeadCorrect(ctx context.Context, func AttestationTarget(ctx context.Context, headersCache *BeaconBlockHeaderCache, chainTime chaintime.Service, - attestation *phase0.Attestation, + attestation *spec.VersionedAttestation, ) ( phase0.Root, error, ) { + attestationData, err := attestation.Data() + if err != nil { + return phase0.Root{}, errors.Wrap(err, "failed to obtain attestation data") + } // Start with first slot of the target epoch. - slot := chainTime.FirstSlotOfEpoch(attestation.Data.Target.Epoch) + slot := chainTime.FirstSlotOfEpoch(attestationData.Target.Epoch) for { header, err := headersCache.Fetch(ctx, slot) if err != nil { @@ -113,13 +127,17 @@ func AttestationTarget(ctx context.Context, func AttestationTargetCorrect(ctx context.Context, headersCache *BeaconBlockHeaderCache, chainTime chaintime.Service, - attestation *phase0.Attestation, + attestation *spec.VersionedAttestation, ) ( bool, error, ) { + attestationData, err := attestation.Data() + if err != nil { + return false, errors.Wrap(err, "failed to obtain attestation data") + } // Start with first slot of the target epoch. - slot := chainTime.FirstSlotOfEpoch(attestation.Data.Target.Epoch) + slot := chainTime.FirstSlotOfEpoch(attestationData.Target.Epoch) for { header, err := headersCache.Fetch(ctx, slot) if err != nil { @@ -135,6 +153,6 @@ func AttestationTargetCorrect(ctx context.Context, slot-- continue } - return bytes.Equal(header.Root[:], attestation.Data.Target.Root[:]), nil + return bytes.Equal(header.Root[:], attestationData.Target.Root[:]), nil } } From 8322353af56393f344d2bb6d99997543a45a6a13 Mon Sep 17 00:00:00 2001 From: Chris Berry Date: Mon, 27 Jan 2025 10:08:48 +0000 Subject: [PATCH 2/4] Update docker file --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c5fd3cd..175bac9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.22-bookworm as builder +FROM golang:1.23-bookworm AS builder WORKDIR /app From f17fe2f5cb3e0c3702a3c50640bfcaa711f690b1 Mon Sep 17 00:00:00 2001 From: Chris Berry Date: Mon, 27 Jan 2025 10:23:30 +0000 Subject: [PATCH 3/4] Refactor based on review --- cmd/block/analyze/command.go | 34 +++++++++++------------ cmd/block/analyze/output.go | 28 +++++++++---------- cmd/block/analyze/process.go | 54 ++++++++++++++++++------------------ 3 files changed, 58 insertions(+), 58 deletions(-) diff --git a/cmd/block/analyze/command.go b/cmd/block/analyze/command.go index 6d4ab18..b67f6a8 100644 --- a/cmd/block/analyze/command.go +++ b/cmd/block/analyze/command.go @@ -55,7 +55,7 @@ type command struct { weightDenominator uint64 // Processing. - priorAttestations map[string]*attestationData + priorAttestations map[string]*attestationDataInfo // Head roots provides the root of the head slot at given slots. headRoots map[phase0.Slot]phase0.Root // Target roots provides the root of the target epoch at given slots. @@ -77,20 +77,20 @@ type blockAnalysis struct { } type attestationAnalysis struct { - Head phase0.Root `json:"head"` - Target phase0.Root `json:"target"` - Distance int `json:"distance"` - Duplicate *attestationData `json:"duplicate,omitempty"` - NewVotes int `json:"new_votes"` - Votes int `json:"votes"` - PossibleVotes int `json:"possible_votes"` - HeadCorrect bool `json:"head_correct"` - HeadTimely bool `json:"head_timely"` - SourceTimely bool `json:"source_timely"` - TargetCorrect bool `json:"target_correct"` - TargetTimely bool `json:"target_timely"` - Score float64 `json:"score"` - Value float64 `json:"value"` + Head phase0.Root `json:"head"` + Target phase0.Root `json:"target"` + Distance int `json:"distance"` + Duplicate *attestationDataInfo `json:"duplicate,omitempty"` + NewVotes int `json:"new_votes"` + Votes int `json:"votes"` + PossibleVotes int `json:"possible_votes"` + HeadCorrect bool `json:"head_correct"` + HeadTimely bool `json:"head_timely"` + SourceTimely bool `json:"source_timely"` + TargetCorrect bool `json:"target_correct"` + TargetTimely bool `json:"target_timely"` + Score float64 `json:"score"` + Value float64 `json:"value"` } type syncCommitteeAnalysis struct { @@ -100,7 +100,7 @@ type syncCommitteeAnalysis struct { Value float64 `json:"value"` } -type attestationData struct { +type attestationDataInfo struct { Block phase0.Slot `json:"block"` Index int `json:"index"` } @@ -110,7 +110,7 @@ func newCommand(_ context.Context) (*command, error) { quiet: viper.GetBool("quiet"), verbose: viper.GetBool("verbose"), debug: viper.GetBool("debug"), - priorAttestations: make(map[string]*attestationData), + priorAttestations: make(map[string]*attestationDataInfo), headRoots: make(map[phase0.Slot]phase0.Root), targetRoots: make(map[phase0.Slot]phase0.Root), votes: make(map[phase0.Slot]map[phase0.CommitteeIndex]bitfield.Bitlist), diff --git a/cmd/block/analyze/output.go b/cmd/block/analyze/output.go index 96e06c2..f68056f 100644 --- a/cmd/block/analyze/output.go +++ b/cmd/block/analyze/output.go @@ -34,20 +34,20 @@ func (c *command) output(ctx context.Context) (string, error) { } type attestationAnalysisJSON struct { - Head string `json:"head"` - Target string `json:"target"` - Distance int `json:"distance"` - Duplicate *attestationData `json:"duplicate,omitempty"` - NewVotes int `json:"new_votes"` - Votes int `json:"votes"` - PossibleVotes int `json:"possible_votes"` - HeadCorrect bool `json:"head_correct"` - HeadTimely bool `json:"head_timely"` - SourceTimely bool `json:"source_timely"` - TargetCorrect bool `json:"target_correct"` - TargetTimely bool `json:"target_timely"` - Score float64 `json:"score"` - Value float64 `json:"value"` + Head string `json:"head"` + Target string `json:"target"` + Distance int `json:"distance"` + Duplicate *attestationDataInfo `json:"duplicate,omitempty"` + NewVotes int `json:"new_votes"` + Votes int `json:"votes"` + PossibleVotes int `json:"possible_votes"` + HeadCorrect bool `json:"head_correct"` + HeadTimely bool `json:"head_timely"` + SourceTimely bool `json:"source_timely"` + TargetCorrect bool `json:"target_correct"` + TargetTimely bool `json:"target_timely"` + Score float64 `json:"score"` + Value float64 `json:"value"` } func (a *attestationAnalysis) MarshalJSON() ([]byte, error) { diff --git a/cmd/block/analyze/process.go b/cmd/block/analyze/process.go index 7e7524a..61e90eb 100644 --- a/cmd/block/analyze/process.go +++ b/cmd/block/analyze/process.go @@ -63,12 +63,12 @@ func (c *command) process(ctx context.Context) error { // Calculate how many parents we need to fetch. minSlot := slot for _, attestation := range attestations { - attestData, err := attestation.Data() + attestationData, err := attestation.Data() if err != nil { return errors.Wrap(err, "failed to obtain attestation data") } - if attestData.Slot < minSlot { - minSlot = attestData.Slot + if attestationData.Slot < minSlot { + minSlot = attestationData.Slot } } if c.debug { @@ -107,14 +107,14 @@ func (c *command) analyzeAttestations(ctx context.Context, block *spec.Versioned if c.debug { fmt.Printf("Processing attestation %d\n", i) } - attestData, err := attestation.Data() + attestationData, err := attestation.Data() if err != nil { return errors.Wrap(err, "failed to obtain attestation data") } analysis := &attestationAnalysis{ - Head: attestData.BeaconBlockRoot, - Target: attestData.Target.Root, - Distance: int(slot - attestData.Slot), + Head: attestationData.BeaconBlockRoot, + Target: attestationData.Target.Root, + Distance: int(slot - attestationData.Slot), } root, err := attestation.HashTreeRoot() @@ -128,13 +128,13 @@ func (c *command) analyzeAttestations(ctx context.Context, block *spec.Versioned if err != nil { return err } - _, exists := blockVotes[attestData.Slot] + _, exists := blockVotes[attestationData.Slot] if !exists { - blockVotes[attestData.Slot] = make(map[phase0.CommitteeIndex]bitfield.Bitlist) + blockVotes[attestationData.Slot] = make(map[phase0.CommitteeIndex]bitfield.Bitlist) } - _, exists = blockVotes[attestData.Slot][attestData.Index] + _, exists = blockVotes[attestationData.Slot][attestationData.Index] if !exists { - blockVotes[attestData.Slot][attestData.Index] = bitfield.NewBitlist(aggregationBits.Len()) + blockVotes[attestationData.Slot][attestationData.Index] = bitfield.NewBitlist(aggregationBits.Len()) } // Count new votes. @@ -142,16 +142,16 @@ func (c *command) analyzeAttestations(ctx context.Context, block *spec.Versioned for j := range aggregationBits.Len() { if aggregationBits.BitAt(j) { analysis.Votes++ - if blockVotes[attestData.Slot][attestData.Index].BitAt(j) { + if blockVotes[attestationData.Slot][attestationData.Index].BitAt(j) { // Already attested to in this block; skip. continue } - if c.votes[attestData.Slot][attestData.Index].BitAt(j) { + if c.votes[attestationData.Slot][attestationData.Index].BitAt(j) { // Already attested to in a previous block; skip. continue } analysis.NewVotes++ - blockVotes[attestData.Slot][attestData.Index].SetBitAt(j, true) + blockVotes[attestationData.Slot][attestationData.Index].SetBitAt(j, true) } } // Calculate head correct. @@ -161,10 +161,10 @@ func (c *command) analyzeAttestations(ctx context.Context, block *spec.Versioned } // Calculate head timely. - analysis.HeadTimely = analysis.HeadCorrect && attestData.Slot == slot-1 + analysis.HeadTimely = analysis.HeadCorrect && attestationData.Slot == slot-1 // Calculate source timely. - analysis.SourceTimely = attestData.Slot >= slot-5 + analysis.SourceTimely = attestationData.Slot >= slot-5 // Calculate target correct. analysis.TargetCorrect, err = c.calcTargetCorrect(ctx, attestation) @@ -174,7 +174,7 @@ func (c *command) analyzeAttestations(ctx context.Context, block *spec.Versioned // Calculate target timely. if block.Version < spec.DataVersionDeneb { - analysis.TargetTimely = attestData.Slot >= slot-32 + analysis.TargetTimely = attestationData.Slot >= slot-32 } else { analysis.TargetTimely = true } @@ -265,7 +265,7 @@ func (c *command) processParentBlock(_ context.Context, block *spec.VersionedSig if err != nil { return err } - c.priorAttestations[fmt.Sprintf("%#x", root)] = &attestationData{ + c.priorAttestations[fmt.Sprintf("%#x", root)] = &attestationDataInfo{ Block: slot, Index: i, } @@ -403,11 +403,11 @@ func (c *command) setup(ctx context.Context) error { } func (c *command) calcHeadCorrect(ctx context.Context, attestation *spec.VersionedAttestation) (bool, error) { - attestData, err := attestation.Data() + attestationData, err := attestation.Data() if err != nil { return false, errors.Wrap(err, "failed to obtain attestation data") } - slot := attestData.Slot + slot := attestationData.Slot root, exists := c.headRoots[slot] if !exists { for { @@ -434,24 +434,24 @@ func (c *command) calcHeadCorrect(ctx context.Context, attestation *spec.Version slot-- continue } - c.headRoots[attestData.Slot] = response.Data.Root + c.headRoots[attestationData.Slot] = response.Data.Root root = response.Data.Root break } } - return bytes.Equal(root[:], attestData.BeaconBlockRoot[:]), nil + return bytes.Equal(root[:], attestationData.BeaconBlockRoot[:]), nil } func (c *command) calcTargetCorrect(ctx context.Context, attestation *spec.VersionedAttestation) (bool, error) { - attestData, err := attestation.Data() + attestationData, err := attestation.Data() if err != nil { return false, errors.Wrap(err, "failed to obtain attestation data") } - root, exists := c.targetRoots[attestData.Slot] + root, exists := c.targetRoots[attestationData.Slot] if !exists { // Start with first slot of the target epoch. - slot := c.chainTime.FirstSlotOfEpoch(attestData.Target.Epoch) + slot := c.chainTime.FirstSlotOfEpoch(attestationData.Target.Epoch) for { response, err := c.blockHeadersProvider.BeaconBlockHeader(ctx, &api.BeaconBlockHeaderOpts{ Block: fmt.Sprintf("%d", slot), @@ -475,12 +475,12 @@ func (c *command) calcTargetCorrect(ctx context.Context, attestation *spec.Versi slot-- continue } - c.targetRoots[attestData.Slot] = response.Data.Root + c.targetRoots[attestationData.Slot] = response.Data.Root root = response.Data.Root break } } - return bytes.Equal(root[:], attestData.Target.Root[:]), nil + return bytes.Equal(root[:], attestationData.Target.Root[:]), nil } func (c *command) analyzeSyncCommittees(_ context.Context, block *spec.VersionedSignedBeaconBlock) error { From f6e23d803b89ad32a4bd1772eebc704bc6c20263 Mon Sep 17 00:00:00 2001 From: Chris Berry Date: Thu, 6 Feb 2025 16:43:26 +0000 Subject: [PATCH 4/4] Update go-eth2-client --- go.mod | 8 ++++---- go.sum | 17 ++++++++--------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index c38bb07..18b7c33 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22.7 toolchain go1.23.2 require ( - github.com/attestantio/go-eth2-client v0.23.1-0.20250127091537-251e60f042d4 + github.com/attestantio/go-eth2-client v0.24.0 github.com/ferranbt/fastssz v0.1.4 github.com/gofrs/uuid v4.4.0+incompatible github.com/google/uuid v1.6.0 @@ -62,7 +62,7 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/magiconair/properties v1.8.9 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/minio/highwayhash v1.0.3 // indirect github.com/minio/sha256-simd v1.0.1 // indirect @@ -92,11 +92,11 @@ require ( go.opentelemetry.io/otel/metric v1.33.0 // indirect go.opentelemetry.io/otel/trace v1.33.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.31.0 // indirect + golang.org/x/crypto v0.32.0 // indirect golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect golang.org/x/net v0.33.0 // indirect golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.29.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241219192143-6b3ec007d9bb // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241219192143-6b3ec007d9bb // indirect google.golang.org/grpc v1.69.2 // indirect diff --git a/go.sum b/go.sum index 7836a12..c749017 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ -github.com/attestantio/go-eth2-client v0.22.0 h1:KmF9kPNNWWGfE7l1BP7pXps4EOXgKnYeFGR0/WbyFhY= -github.com/attestantio/go-eth2-client v0.22.0/go.mod h1:d7ZPNrMX8jLfIgML5u7QZxFo2AukLM+5m08iMaLdqb8= -github.com/attestantio/go-eth2-client v0.23.1-0.20250127091537-251e60f042d4 h1:ePstQwO3RoDX2am93bvtUNLsMtxlikx9kPJDdzr9sk8= -github.com/attestantio/go-eth2-client v0.23.1-0.20250127091537-251e60f042d4/go.mod h1:vy5jU/uDZ2+RcVzq5BfnG+bQ3/6uu9DGwCrGsPtjJ1A= +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/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -78,8 +76,9 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -215,8 +214,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo= golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -231,8 +230,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=