diff --git a/CHANGELOG.md b/CHANGELOG.md index f83e404..ae093c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +dev: + - provide ETH values as well as validator numbers in "epoch summary" + 1.37.3: - add "hoodi" to the list of supported networks diff --git a/cmd/epoch/summary/command.go b/cmd/epoch/summary/command.go index ff1d736..7bd8bdd 100644 --- a/cmd/epoch/summary/command.go +++ b/cmd/epoch/summary/command.go @@ -15,9 +15,11 @@ package epochsummary import ( "context" + "math/big" "time" eth2client "github.com/attestantio/go-eth2-client" + 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" @@ -52,6 +54,16 @@ type command struct { beaconCommitteesProvider eth2client.BeaconCommitteesProvider beaconBlockHeadersProvider eth2client.BeaconBlockHeadersProvider + // Intermediate data. + validatorInfo map[phase0.ValidatorIndex]*apiv1.Validator + participatingValidators map[phase0.ValidatorIndex]struct{} + headCorrectValidators map[phase0.ValidatorIndex]struct{} + headTimelyValidators map[phase0.ValidatorIndex]struct{} + sourceTimelyValidators map[phase0.ValidatorIndex]struct{} + targetCorrectValidators map[phase0.ValidatorIndex]struct{} + targetTimelyValidators map[phase0.ValidatorIndex]struct{} + participations map[phase0.ValidatorIndex]*attestingValidator + // Caches. blocksCache map[string]*spec.VersionedSignedBeaconBlock @@ -68,12 +80,19 @@ type epochSummary struct { SyncCommitteeValidators int `json:"sync_committee_validators"` SyncCommittee []*epochSyncCommittee `json:"sync_committees"` ActiveValidators int `json:"active_validators"` + ActiveBalance *big.Int `json:"active_balance"` ParticipatingValidators int `json:"participating_validators"` + ParticipatingBalance *big.Int `json:"participating_balance"` HeadCorrectValidators int `json:"head_correct_validators"` + HeadCorrectBalance *big.Int `json:"head_correct_balance"` HeadTimelyValidators int `json:"head_timely_validators"` + HeadTimelyBalance *big.Int `json:"head_timely_balance"` SourceTimelyValidators int `json:"source_timely_validators"` + SourceTimelyBalance *big.Int `json:"source_timely_balance"` TargetCorrectValidators int `json:"target_correct_validators"` + TargetCorrectBalance *big.Int `json:"target_correct_balance"` TargetTimelyValidators int `json:"target_timely_validators"` + TargetTimelyBalance *big.Int `json:"target_timely_balance"` NonParticipatingValidators []*attestingValidator `json:"nonparticipating_validators"` NonHeadCorrectValidators []*attestingValidator `json:"nonheadcorrect_validators"` NonHeadTimelyValidators []*attestingValidator `json:"nonheadtimely_validators"` @@ -95,14 +114,15 @@ type epochSyncCommittee struct { } type attestingValidator struct { - Validator phase0.ValidatorIndex `json:"validator_index"` - Slot phase0.Slot `json:"slot"` - Committee phase0.CommitteeIndex `json:"committee_index"` - HeadVote *phase0.Root `json:"head_vote,omitempty"` - Head *phase0.Root `json:"head,omitempty"` - TargetVote *phase0.Root `json:"target_vote,omitempty"` - Target *phase0.Root `json:"target,omitempty"` - InclusionSlot phase0.Slot `json:"inclusion_slot,omitempty"` + Validator phase0.ValidatorIndex `json:"validator_index"` + EffectiveBalance phase0.Gwei `json:"effective_balance"` + Slot phase0.Slot `json:"slot"` + Committee phase0.CommitteeIndex `json:"committee_index"` + HeadVote *phase0.Root `json:"head_vote,omitempty"` + Head *phase0.Root `json:"head,omitempty"` + TargetVote *phase0.Root `json:"target_vote,omitempty"` + Target *phase0.Root `json:"target,omitempty"` + InclusionSlot phase0.Slot `json:"inclusion_slot,omitempty"` } func newCommand(_ context.Context) (*command, error) { @@ -112,10 +132,24 @@ func newCommand(_ context.Context) (*command, error) { debug: viper.GetBool("debug"), validatorsStr: viper.GetStringSlice("validators"), summary: &epochSummary{ - Proposals: make([]*epochProposal, 0), + Proposals: make([]*epochProposal, 0), + ActiveBalance: big.NewInt(0), + ParticipatingBalance: big.NewInt(0), + HeadCorrectBalance: big.NewInt(0), + HeadTimelyBalance: big.NewInt(0), + SourceTimelyBalance: big.NewInt(0), + TargetCorrectBalance: big.NewInt(0), + TargetTimelyBalance: big.NewInt(0), }, - validators: make(map[phase0.ValidatorIndex]struct{}), - blocksCache: make(map[string]*spec.VersionedSignedBeaconBlock), + validators: make(map[phase0.ValidatorIndex]struct{}), + participatingValidators: make(map[phase0.ValidatorIndex]struct{}), + headCorrectValidators: make(map[phase0.ValidatorIndex]struct{}), + headTimelyValidators: make(map[phase0.ValidatorIndex]struct{}), + sourceTimelyValidators: make(map[phase0.ValidatorIndex]struct{}), + targetCorrectValidators: make(map[phase0.ValidatorIndex]struct{}), + targetTimelyValidators: make(map[phase0.ValidatorIndex]struct{}), + participations: make(map[phase0.ValidatorIndex]*attestingValidator), + blocksCache: make(map[string]*spec.VersionedSignedBeaconBlock), } // Timeout. diff --git a/cmd/epoch/summary/output.go b/cmd/epoch/summary/output.go index 6fb5022..4535095 100644 --- a/cmd/epoch/summary/output.go +++ b/cmd/epoch/summary/output.go @@ -1,4 +1,4 @@ -// Copyright © 2022 Weald Technology Trading. +// Copyright © 2022, 2025 Weald Technology Trading. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -17,6 +17,7 @@ import ( "context" "encoding/json" "fmt" + "math/big" "strings" ) @@ -69,12 +70,80 @@ func (c *command) outputTxt(_ context.Context) (string, error) { } } - builder.WriteString(fmt.Sprintf("\n Attestations: %d/%d (%0.2f%%)", c.summary.ParticipatingValidators, c.summary.ActiveValidators, 100.0*float64(c.summary.ParticipatingValidators)/float64(c.summary.ActiveValidators))) - builder.WriteString(fmt.Sprintf("\n Source timely: %d/%d (%0.2f%%)", c.summary.SourceTimelyValidators, c.summary.ActiveValidators, 100.0*float64(c.summary.SourceTimelyValidators)/float64(c.summary.ActiveValidators))) - builder.WriteString(fmt.Sprintf("\n Target correct: %d/%d (%0.2f%%)", c.summary.TargetCorrectValidators, c.summary.ActiveValidators, 100.0*float64(c.summary.TargetCorrectValidators)/float64(c.summary.ActiveValidators))) - builder.WriteString(fmt.Sprintf("\n Target timely: %d/%d (%0.2f%%)", c.summary.TargetTimelyValidators, c.summary.ActiveValidators, 100.0*float64(c.summary.TargetTimelyValidators)/float64(c.summary.ActiveValidators))) - builder.WriteString(fmt.Sprintf("\n Head correct: %d/%d (%0.2f%%)", c.summary.HeadCorrectValidators, c.summary.ActiveValidators, 100.0*float64(c.summary.HeadCorrectValidators)/float64(c.summary.ActiveValidators))) - builder.WriteString(fmt.Sprintf("\n Head timely: %d/%d (%0.2f%%)", c.summary.HeadTimelyValidators, c.summary.ActiveValidators, 100.0*float64(c.summary.HeadTimelyValidators)/float64(c.summary.ActiveValidators))) + gweiToEth := big.NewInt(1e9) + mul := big.NewInt(10000) + participatingBalancePct := new(big.Int).Div(new(big.Int).Mul(c.summary.ParticipatingBalance, mul), c.summary.ActiveBalance) + builder.WriteString(fmt.Sprintf("\n Attesting balance: %s/%s (%0.2f%%)", + new(big.Int).Div(c.summary.ParticipatingBalance, gweiToEth).String(), + new(big.Int).Div(c.summary.ActiveBalance, gweiToEth).String(), + float64(participatingBalancePct.Uint64())/100.0, + )) + + sourceTimelyBalancePct := new(big.Int).Div(new(big.Int).Mul(c.summary.SourceTimelyBalance, mul), c.summary.ActiveBalance) + builder.WriteString(fmt.Sprintf("\n Source timely: %s/%s (%0.2f%%)", + new(big.Int).Div(c.summary.SourceTimelyBalance, gweiToEth).String(), + new(big.Int).Div(c.summary.ActiveBalance, gweiToEth).String(), + float64(sourceTimelyBalancePct.Uint64())/100.0, + )) + + targetCorrectBalancePct := new(big.Int).Div(new(big.Int).Mul(c.summary.TargetCorrectBalance, mul), c.summary.ActiveBalance) + builder.WriteString(fmt.Sprintf("\n Target correct: %s/%s (%0.2f%%)", + new(big.Int).Div(c.summary.TargetCorrectBalance, gweiToEth).String(), + new(big.Int).Div(c.summary.ActiveBalance, gweiToEth).String(), + float64(targetCorrectBalancePct.Uint64())/100.0, + )) + + targetTimelyBalancePct := new(big.Int).Div(new(big.Int).Mul(c.summary.TargetTimelyBalance, mul), c.summary.ActiveBalance) + builder.WriteString(fmt.Sprintf("\n Target timely: %s/%s (%0.2f%%)", + new(big.Int).Div(c.summary.TargetTimelyBalance, gweiToEth).String(), + new(big.Int).Div(c.summary.ActiveBalance, gweiToEth).String(), + float64(targetTimelyBalancePct.Uint64())/100.0, + )) + + headCorrectBalancePct := new(big.Int).Div(new(big.Int).Mul(c.summary.HeadCorrectBalance, mul), c.summary.ActiveBalance) + builder.WriteString(fmt.Sprintf("\n Head correct: %s/%s (%0.2f%%)", + new(big.Int).Div(c.summary.HeadCorrectBalance, gweiToEth).String(), + new(big.Int).Div(c.summary.ActiveBalance, gweiToEth).String(), + float64(headCorrectBalancePct.Uint64())/100.0, + )) + + headTimelyBalancePct := new(big.Int).Div(new(big.Int).Mul(c.summary.HeadTimelyBalance, mul), c.summary.ActiveBalance) + builder.WriteString(fmt.Sprintf("\n Head timely: %s/%s (%0.2f%%)", + new(big.Int).Div(c.summary.HeadTimelyBalance, gweiToEth).String(), + new(big.Int).Div(c.summary.ActiveBalance, gweiToEth).String(), + float64(headTimelyBalancePct.Uint64())/100.0, + )) + + builder.WriteString(fmt.Sprintf("\n Attesting validators: %d/%d (%0.2f%%)", + c.summary.ParticipatingValidators, + c.summary.ActiveValidators, + 100.0*float64(c.summary.ParticipatingValidators)/float64(c.summary.ActiveValidators), + )) + builder.WriteString(fmt.Sprintf("\n Source timely: %d/%d (%0.2f%%)", + c.summary.SourceTimelyValidators, + c.summary.ActiveValidators, + 100.0*float64(c.summary.SourceTimelyValidators)/float64(c.summary.ActiveValidators), + )) + builder.WriteString(fmt.Sprintf("\n Target correct: %d/%d (%0.2f%%)", + c.summary.TargetCorrectValidators, + c.summary.ActiveValidators, + 100.0*float64(c.summary.TargetCorrectValidators)/float64(c.summary.ActiveValidators), + )) + builder.WriteString(fmt.Sprintf("\n Target timely: %d/%d (%0.2f%%)", + c.summary.TargetTimelyValidators, + c.summary.ActiveValidators, + 100.0*float64(c.summary.TargetTimelyValidators)/float64(c.summary.ActiveValidators), + )) + builder.WriteString(fmt.Sprintf("\n Head correct: %d/%d (%0.2f%%)", + c.summary.HeadCorrectValidators, + c.summary.ActiveValidators, + 100.0*float64(c.summary.HeadCorrectValidators)/float64(c.summary.ActiveValidators), + )) + builder.WriteString(fmt.Sprintf("\n Head timely: %d/%d (%0.2f%%)", + c.summary.HeadTimelyValidators, + c.summary.ActiveValidators, + 100.0*float64(c.summary.HeadTimelyValidators)/float64(c.summary.ActiveValidators), + )) if c.verbose { // Sort list by validator index. for _, validator := range c.summary.NonParticipatingValidators { diff --git a/cmd/epoch/summary/process.go b/cmd/epoch/summary/process.go index ec60071..4f4255a 100644 --- a/cmd/epoch/summary/process.go +++ b/cmd/epoch/summary/process.go @@ -1,4 +1,4 @@ -// Copyright © 2022, 2023 Weald Technology Trading. +// Copyright © 2022 - 2025 Weald Technology Trading. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -16,6 +16,7 @@ package epochsummary import ( "context" "fmt" + "math/big" "net/http" "sort" @@ -105,12 +106,14 @@ func (c *command) activeValidators(ctx context.Context) (map[phase0.ValidatorInd } response, err := c.validatorsProvider.Validators(ctx, &api.ValidatorsOpts{ - State: "head", + State: fmt.Sprintf("%d", c.chainTime.FirstSlotOfEpoch(c.summary.Epoch)), Indices: validatorIndices, }) if err != nil { return nil, errors.Wrap(err, "failed to obtain validators for epoch") } + c.validatorInfo = response.Data + activeValidators := make(map[phase0.ValidatorIndex]*apiv1.Validator) for _, validator := range response.Data { _, exists := c.validators[validator.Index] @@ -133,6 +136,10 @@ func (c *command) processAttesterDuties(ctx context.Context) error { } c.summary.ActiveValidators = len(activeValidators) + for _, validator := range activeValidators { + c.summary.ActiveBalance = c.summary.ActiveBalance.Add(c.summary.ActiveBalance, big.NewInt(int64(validator.Validator.EffectiveBalance))) + } + // Obtain number of validators that voted for blocks in the epoch. // These votes can be included anywhere from the second slot of // the epoch to the first slot of the next-but-one epoch. @@ -142,43 +149,42 @@ func (c *command) processAttesterDuties(ctx context.Context) error { lastSlot = c.chainTime.CurrentSlot() } - participatingValidators, headCorrectValidators, headTimelyValidators, sourceTimelyValidators, targetCorrectValidators, targetTimelyValidators, participations, err := c.processSlots(ctx, firstSlot, lastSlot) - if err != nil { + if err := c.processSlots(ctx, firstSlot, lastSlot); err != nil { return err } - c.summary.ParticipatingValidators = len(participatingValidators) - c.summary.HeadCorrectValidators = len(headCorrectValidators) - c.summary.HeadTimelyValidators = len(headTimelyValidators) - c.summary.SourceTimelyValidators = len(sourceTimelyValidators) - c.summary.TargetCorrectValidators = len(targetCorrectValidators) - c.summary.TargetTimelyValidators = len(targetTimelyValidators) + c.summary.ParticipatingValidators = len(c.participatingValidators) + c.summary.HeadCorrectValidators = len(c.headCorrectValidators) + c.summary.HeadTimelyValidators = len(c.headTimelyValidators) + c.summary.SourceTimelyValidators = len(c.sourceTimelyValidators) + c.summary.TargetCorrectValidators = len(c.targetCorrectValidators) + c.summary.TargetTimelyValidators = len(c.targetTimelyValidators) - c.summary.NonParticipatingValidators = make([]*attestingValidator, 0, len(activeValidators)-len(participatingValidators)) + c.summary.NonParticipatingValidators = make([]*attestingValidator, 0, len(activeValidators)-len(c.participatingValidators)) for activeValidatorIndex := range activeValidators { - if _, exists := participatingValidators[activeValidatorIndex]; !exists { - if _, exists := participations[activeValidatorIndex]; exists { - c.summary.NonParticipatingValidators = append(c.summary.NonParticipatingValidators, participations[activeValidatorIndex]) + if _, exists := c.participatingValidators[activeValidatorIndex]; !exists { + if _, exists := c.participations[activeValidatorIndex]; exists { + c.summary.NonParticipatingValidators = append(c.summary.NonParticipatingValidators, c.participations[activeValidatorIndex]) } } - if _, exists := headCorrectValidators[activeValidatorIndex]; !exists { - if _, exists := participations[activeValidatorIndex]; exists { - c.summary.NonHeadCorrectValidators = append(c.summary.NonHeadCorrectValidators, participations[activeValidatorIndex]) + if _, exists := c.headCorrectValidators[activeValidatorIndex]; !exists { + if _, exists := c.participations[activeValidatorIndex]; exists { + c.summary.NonHeadCorrectValidators = append(c.summary.NonHeadCorrectValidators, c.participations[activeValidatorIndex]) } } - if _, exists := headTimelyValidators[activeValidatorIndex]; !exists { - if _, exists := participations[activeValidatorIndex]; exists { - c.summary.NonHeadTimelyValidators = append(c.summary.NonHeadTimelyValidators, participations[activeValidatorIndex]) + if _, exists := c.headTimelyValidators[activeValidatorIndex]; !exists { + if _, exists := c.participations[activeValidatorIndex]; exists { + c.summary.NonHeadTimelyValidators = append(c.summary.NonHeadTimelyValidators, c.participations[activeValidatorIndex]) } } - if _, exists := targetCorrectValidators[activeValidatorIndex]; !exists { - if _, exists := participations[activeValidatorIndex]; exists { - c.summary.NonTargetCorrectValidators = append(c.summary.NonTargetCorrectValidators, participations[activeValidatorIndex]) + if _, exists := c.targetCorrectValidators[activeValidatorIndex]; !exists { + if _, exists := c.participations[activeValidatorIndex]; exists { + c.summary.NonTargetCorrectValidators = append(c.summary.NonTargetCorrectValidators, c.participations[activeValidatorIndex]) } } - if _, exists := sourceTimelyValidators[activeValidatorIndex]; !exists { - if _, exists := participations[activeValidatorIndex]; exists { - c.summary.NonSourceTimelyValidators = append(c.summary.NonSourceTimelyValidators, participations[activeValidatorIndex]) + if _, exists := c.sourceTimelyValidators[activeValidatorIndex]; !exists { + if _, exists := c.participations[activeValidatorIndex]; exists { + c.summary.NonSourceTimelyValidators = append(c.summary.NonSourceTimelyValidators, c.participations[activeValidatorIndex]) } } } @@ -199,24 +205,8 @@ func (c *command) processAttesterDuties(ctx context.Context) error { func (c *command) processSlots(ctx context.Context, firstSlot phase0.Slot, lastSlot phase0.Slot, -) ( - map[phase0.ValidatorIndex]struct{}, - map[phase0.ValidatorIndex]struct{}, - map[phase0.ValidatorIndex]struct{}, - map[phase0.ValidatorIndex]struct{}, - map[phase0.ValidatorIndex]struct{}, - map[phase0.ValidatorIndex]struct{}, - map[phase0.ValidatorIndex]*attestingValidator, - error, -) { - votes := make(map[phase0.ValidatorIndex]struct{}) - headCorrects := make(map[phase0.ValidatorIndex]struct{}) - headTimelys := make(map[phase0.ValidatorIndex]struct{}) - sourceTimelys := make(map[phase0.ValidatorIndex]struct{}) - targetCorrects := make(map[phase0.ValidatorIndex]struct{}) - targetTimelys := make(map[phase0.ValidatorIndex]struct{}) +) error { allCommittees := make(map[phase0.Slot]map[phase0.CommitteeIndex][]phase0.ValidatorIndex) - participations := make(map[phase0.ValidatorIndex]*attestingValidator) // Need a cache of beacon block headers to reduce lookup times. headersCache := util.NewBeaconBlockHeaderCache(c.beaconBlockHeadersProvider) @@ -224,7 +214,7 @@ func (c *command) processSlots(ctx context.Context, for slot := firstSlot; slot <= lastSlot; slot++ { block, err := c.fetchBlock(ctx, fmt.Sprintf("%d", slot)) if err != nil { - return nil, nil, nil, nil, nil, nil, nil, errors.Wrap(err, fmt.Sprintf("failed to obtain block for slot %d", slot)) + return errors.Wrap(err, fmt.Sprintf("failed to obtain block for slot %d", slot)) } if block == nil { // No block at this slot; that's fine. @@ -232,16 +222,16 @@ func (c *command) processSlots(ctx context.Context, } slot, err := block.Slot() if err != nil { - return nil, nil, nil, nil, nil, nil, nil, err + return err } attestations, err := block.Attestations() if err != nil { - return nil, nil, nil, nil, nil, nil, nil, err + return err } for _, attestation := range attestations { attestationData, err := attestation.Data() if err != nil { - return nil, nil, nil, nil, nil, nil, nil, errors.Wrap(err, "failed to obtain attestation data") + return 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. @@ -253,7 +243,7 @@ func (c *command) processSlots(ctx context.Context, 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", attestationData.Slot)) + return 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 { @@ -270,94 +260,71 @@ func (c *command) processSlots(ctx context.Context, } } - if _, exists := participations[index]; !exists { - participations[index] = &attestingValidator{ - Validator: index, - Slot: beaconCommittee.Slot, - Committee: beaconCommittee.Index, + if _, exists := c.participations[index]; !exists { + c.participations[index] = &attestingValidator{ + Validator: index, + EffectiveBalance: c.validatorInfo[index].Validator.EffectiveBalance, + Slot: beaconCommittee.Slot, + Committee: beaconCommittee.Index, } } } } slotCommittees = allCommittees[attestationData.Slot] } - if attestation.Version >= spec.DataVersionElectra { - participations, votes, headCorrects, headTimelys, sourceTimelys, targetCorrects, targetTimelys, err = c.extractElectraAttestationData( - ctx, attestation, attestationData, slotCommittees, slot, headersCache, participations, votes, headCorrects, headTimelys, sourceTimelys, targetCorrects, targetTimelys) - if err != nil { - return nil, nil, nil, nil, nil, nil, nil, err - } - } else { - participations, votes, headCorrects, headTimelys, sourceTimelys, targetCorrects, targetTimelys, err = c.extractPhase0AttestationData( - ctx, attestation, attestationData, slotCommittees, slot, headersCache, participations, votes, headCorrects, headTimelys, sourceTimelys, targetCorrects, targetTimelys) - if err != nil { - return nil, nil, nil, nil, nil, nil, nil, err - } + if err := c.extractAttestationData(ctx, attestation, attestationData, slotCommittees, slot, headersCache); err != nil { + return err } } } - return votes, - headCorrects, - headTimelys, - sourceTimelys, - targetCorrects, - targetTimelys, - participations, - nil + return nil } -func (c *command) extractPhase0AttestationData(ctx context.Context, +func (c *command) extractAttestationData(ctx context.Context, attestation *spec.VersionedAttestation, attestationData *phase0.AttestationData, slotCommittees map[phase0.CommitteeIndex][]phase0.ValidatorIndex, slot phase0.Slot, headersCache *util.BeaconBlockHeaderCache, - participations map[phase0.ValidatorIndex]*attestingValidator, - votes map[phase0.ValidatorIndex]struct{}, - headCorrects map[phase0.ValidatorIndex]struct{}, - headTimelys map[phase0.ValidatorIndex]struct{}, - sourceTimelys map[phase0.ValidatorIndex]struct{}, - targetCorrects map[phase0.ValidatorIndex]struct{}, - targetTimelys map[phase0.ValidatorIndex]struct{}, -) ( - map[phase0.ValidatorIndex]*attestingValidator, - map[phase0.ValidatorIndex]struct{}, - map[phase0.ValidatorIndex]struct{}, - map[phase0.ValidatorIndex]struct{}, - map[phase0.ValidatorIndex]struct{}, - map[phase0.ValidatorIndex]struct{}, - map[phase0.ValidatorIndex]struct{}, - error, -) { - committee := slotCommittees[attestationData.Index] - +) error { inclusionDistance := slot - attestationData.Slot head, err := util.AttestationHead(ctx, headersCache, attestation) if err != nil { - return nil, nil, nil, nil, nil, nil, nil, err + return err } headCorrect, err := util.AttestationHeadCorrect(ctx, headersCache, attestation) if err != nil { - return nil, nil, nil, nil, nil, nil, nil, err + return err } target, err := util.AttestationTarget(ctx, headersCache, c.chainTime, attestation) if err != nil { - return nil, nil, nil, nil, nil, nil, nil, err + return err } targetCorrect, err := util.AttestationTargetCorrect(ctx, headersCache, c.chainTime, attestation) if err != nil { - return nil, nil, nil, nil, nil, nil, nil, err + return err + } + + committee := slotCommittees[attestationData.Index] + // Update with all of the committees if we have committee bits (from Electra onwards). + committeeBits, err := attestation.CommitteeBits() + if err == nil { + committee = make([]phase0.ValidatorIndex, 0) + for _, index := range committeeBits.BitIndices() { + committee = append(committee, slotCommittees[phase0.CommitteeIndex(index)]...) + } } aggregationBits, err := attestation.AggregationBits() if err != nil { - return nil, nil, nil, nil, nil, nil, nil, errors.Wrap(err, "failed to obtain aggregation bits") + return errors.Wrap(err, "failed to obtain aggregation bits") } + for i := range aggregationBits.Len() { if aggregationBits.BitAt(i) { - validatorIndex := committee[int(i)] + validatorIndex := committee[i] if len(c.validators) > 0 { if _, exists := c.validators[validatorIndex]; !exists { // Not one of our validators. @@ -366,140 +333,43 @@ func (c *command) extractPhase0AttestationData(ctx context.Context, } // Only set the information from the first attestation we find for this validator. - if participations[validatorIndex].InclusionSlot == 0 { - participations[validatorIndex].HeadVote = &attestationData.BeaconBlockRoot - participations[validatorIndex].Head = &head - participations[validatorIndex].TargetVote = &attestationData.Target.Root - participations[validatorIndex].Target = &target - participations[validatorIndex].InclusionSlot = slot + if c.participations[validatorIndex].InclusionSlot == 0 { + c.participations[validatorIndex].HeadVote = &attestationData.BeaconBlockRoot + c.participations[validatorIndex].Head = &head + c.participations[validatorIndex].TargetVote = &attestationData.Target.Root + c.participations[validatorIndex].Target = &target + c.participations[validatorIndex].InclusionSlot = slot } - votes[validatorIndex] = struct{}{} - if _, exists := headCorrects[validatorIndex]; !exists && headCorrect { - headCorrects[validatorIndex] = struct{}{} + validatorBalance := big.NewInt(int64(c.validatorInfo[validatorIndex].Validator.EffectiveBalance)) + if _, exists := c.participatingValidators[validatorIndex]; !exists { + c.summary.ParticipatingBalance = c.summary.ParticipatingBalance.Add(c.summary.ParticipatingBalance, validatorBalance) + c.participatingValidators[validatorIndex] = struct{}{} } - if _, exists := headTimelys[validatorIndex]; !exists && headCorrect && inclusionDistance == 1 { - headTimelys[validatorIndex] = struct{}{} + if _, exists := c.headCorrectValidators[validatorIndex]; !exists && headCorrect { + c.headCorrectValidators[validatorIndex] = struct{}{} + c.summary.HeadCorrectBalance = c.summary.HeadCorrectBalance.Add(c.summary.HeadCorrectBalance, validatorBalance) } - if _, exists := sourceTimelys[validatorIndex]; !exists && inclusionDistance <= 5 { - sourceTimelys[validatorIndex] = struct{}{} + if _, exists := c.headTimelyValidators[validatorIndex]; !exists && headCorrect && inclusionDistance == 1 { + c.headTimelyValidators[validatorIndex] = struct{}{} + c.summary.HeadTimelyBalance = c.summary.HeadTimelyBalance.Add(c.summary.HeadTimelyBalance, validatorBalance) } - if _, exists := targetCorrects[validatorIndex]; !exists && targetCorrect { - targetCorrects[validatorIndex] = struct{}{} + if _, exists := c.sourceTimelyValidators[validatorIndex]; !exists && inclusionDistance <= 5 { + c.sourceTimelyValidators[validatorIndex] = struct{}{} + c.summary.SourceTimelyBalance = c.summary.SourceTimelyBalance.Add(c.summary.SourceTimelyBalance, validatorBalance) } - if _, exists := targetTimelys[validatorIndex]; !exists && targetCorrect && inclusionDistance <= 32 { - targetTimelys[validatorIndex] = struct{}{} + if _, exists := c.targetCorrectValidators[validatorIndex]; !exists && targetCorrect { + c.targetCorrectValidators[validatorIndex] = struct{}{} + c.summary.TargetCorrectBalance = c.summary.TargetCorrectBalance.Add(c.summary.TargetCorrectBalance, validatorBalance) + } + if _, exists := c.targetTimelyValidators[validatorIndex]; !exists && targetCorrect && inclusionDistance <= 32 { + c.targetTimelyValidators[validatorIndex] = struct{}{} + c.summary.TargetTimelyBalance = c.summary.TargetTimelyBalance.Add(c.summary.TargetTimelyBalance, validatorBalance) } } } - return participations, votes, headCorrects, headTimelys, sourceTimelys, targetCorrects, targetTimelys, err -} -func (c *command) extractElectraAttestationData(ctx context.Context, - attestation *spec.VersionedAttestation, - attestationData *phase0.AttestationData, - slotCommittees map[phase0.CommitteeIndex][]phase0.ValidatorIndex, - slot phase0.Slot, - headersCache *util.BeaconBlockHeaderCache, - participations map[phase0.ValidatorIndex]*attestingValidator, - votes map[phase0.ValidatorIndex]struct{}, - headCorrects map[phase0.ValidatorIndex]struct{}, - headTimelys map[phase0.ValidatorIndex]struct{}, - sourceTimelys map[phase0.ValidatorIndex]struct{}, - targetCorrects map[phase0.ValidatorIndex]struct{}, - targetTimelys map[phase0.ValidatorIndex]struct{}, -) ( - map[phase0.ValidatorIndex]*attestingValidator, - map[phase0.ValidatorIndex]struct{}, - map[phase0.ValidatorIndex]struct{}, - map[phase0.ValidatorIndex]struct{}, - map[phase0.ValidatorIndex]struct{}, - map[phase0.ValidatorIndex]struct{}, - map[phase0.ValidatorIndex]struct{}, - error, -) { - committeeBits, err := attestation.CommitteeBits() - if err != nil { - return nil, nil, nil, nil, nil, nil, nil, errors.Wrap(err, "failed to obtain committee bits") - } - for _, committeeIndex := range committeeBits.BitIndices() { - committee := slotCommittees[phase0.CommitteeIndex(committeeIndex)] - - inclusionDistance := slot - attestationData.Slot - - head, err := util.AttestationHead(ctx, headersCache, attestation) - if err != nil { - return nil, nil, nil, nil, nil, nil, nil, err - } - headCorrect, err := util.AttestationHeadCorrect(ctx, headersCache, attestation) - if err != nil { - return nil, nil, nil, nil, nil, nil, nil, err - } - target, err := util.AttestationTarget(ctx, headersCache, c.chainTime, attestation) - if err != nil { - return nil, nil, nil, nil, nil, nil, nil, err - } - targetCorrect, err := util.AttestationTargetCorrect(ctx, headersCache, c.chainTime, attestation) - if err != nil { - return nil, nil, nil, nil, nil, nil, nil, err - } - - aggregationBits, err := attestation.AggregationBits() - if err != nil { - return nil, nil, nil, nil, nil, nil, nil, errors.Wrap(err, "failed to obtain aggregation bits") - } - // Calculate the offset for the committee so we can extract the validator from the aggregate_bits. - committeeOffset := calcCommitteeOffset(phase0.CommitteeIndex(committeeIndex), slotCommittees) - - // Range over the committee rather than the aggregate_bits as it's the smaller set. - for i := range committee { - aggregateIndex := committeeOffset + uint64(i) - if aggregationBits.BitAt(aggregateIndex) { - validatorIndex := committee[i] - if len(c.validators) > 0 { - if _, exists := c.validators[validatorIndex]; !exists { - // Not one of our validators. - continue - } - } - - // Only set the information from the first attestation we find for this validator. - if participations[validatorIndex].InclusionSlot == 0 { - participations[validatorIndex].HeadVote = &attestationData.BeaconBlockRoot - participations[validatorIndex].Head = &head - participations[validatorIndex].TargetVote = &attestationData.Target.Root - participations[validatorIndex].Target = &target - participations[validatorIndex].InclusionSlot = slot - } - - votes[validatorIndex] = struct{}{} - if _, exists := headCorrects[validatorIndex]; !exists && headCorrect { - headCorrects[validatorIndex] = struct{}{} - } - if _, exists := headTimelys[validatorIndex]; !exists && headCorrect && inclusionDistance == 1 { - headTimelys[validatorIndex] = struct{}{} - } - if _, exists := sourceTimelys[validatorIndex]; !exists && inclusionDistance <= 5 { - sourceTimelys[validatorIndex] = struct{}{} - } - if _, exists := targetCorrects[validatorIndex]; !exists && targetCorrect { - targetCorrects[validatorIndex] = struct{}{} - } - if _, exists := targetTimelys[validatorIndex]; !exists && targetCorrect && inclusionDistance <= 32 { - targetTimelys[validatorIndex] = struct{}{} - } - } - } - } - return participations, votes, headCorrects, headTimelys, sourceTimelys, targetCorrects, targetTimelys, err -} - -func calcCommitteeOffset(committeeIndex phase0.CommitteeIndex, slotCommittees map[phase0.CommitteeIndex][]phase0.ValidatorIndex) uint64 { - var total uint64 - for i := range committeeIndex { - total += uint64(len(slotCommittees[i])) - } - return total + return nil } func (c *command) processSyncCommitteeDuties(ctx context.Context) error {