Add ETH values to epoch summary.

This commit is contained in:
Jim McDonald
2025-04-03 10:13:45 +01:00
parent 25a5bd917f
commit c46483586d
4 changed files with 217 additions and 241 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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 {

View File

@@ -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 {