mirror of
https://github.com/wealdtech/ethdo.git
synced 2026-01-07 21:24:01 -05:00
More data for epoch summaries.
This commit is contained in:
@@ -1,3 +1,12 @@
|
||||
1.36.1:
|
||||
- more JSON data for epoch summary
|
||||
|
||||
1.36.0:
|
||||
- support keystore wallets
|
||||
|
||||
1.35.6:
|
||||
- provide more JSON data in "epoch summary"
|
||||
|
||||
1.35.5:
|
||||
- allow keystore to be output to the console
|
||||
|
||||
|
||||
@@ -29,12 +29,12 @@ func init() {
|
||||
RootCmd.AddCommand(epochCmd)
|
||||
}
|
||||
|
||||
func epochFlags(_ *cobra.Command) {
|
||||
epochSummaryCmd.Flags().String("epoch", "", "the epoch for which to obtain information (default current, can be 'current', 'last' or a number)")
|
||||
func epochFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().String("epoch", "", "the epoch for which to obtain information (default current, can be 'current', 'last' or a number)")
|
||||
}
|
||||
|
||||
func epochBindings(cmd *cobra.Command) {
|
||||
if err := viper.BindPFlag("epoch", cmd.Flags().Lookup("epoch")); err != nil {
|
||||
if err := viper.BindPFlag("validators", cmd.Flags().Lookup("validators")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,9 +36,11 @@ type command struct {
|
||||
allowInsecureConnections bool
|
||||
|
||||
// Operation.
|
||||
epoch string
|
||||
stream bool
|
||||
jsonOutput bool
|
||||
epoch string
|
||||
validatorsStr []string
|
||||
validators map[phase0.ValidatorIndex]struct{}
|
||||
stream bool
|
||||
jsonOutput bool
|
||||
|
||||
// Data access.
|
||||
eth2Client eth2client.Service
|
||||
@@ -58,45 +60,61 @@ type command struct {
|
||||
}
|
||||
|
||||
type epochSummary struct {
|
||||
Epoch phase0.Epoch `json:"epoch"`
|
||||
FirstSlot phase0.Slot `json:"first_slot"`
|
||||
LastSlot phase0.Slot `json:"last_slot"`
|
||||
Proposals []*epochProposal `json:"proposals"`
|
||||
SyncCommittee []*epochSyncCommittee `json:"sync_committees"`
|
||||
ActiveValidators int `json:"active_validators"`
|
||||
ParticipatingValidators int `json:"participating_validators"`
|
||||
HeadCorrectValidators int `json:"head_correct_validators"`
|
||||
HeadTimelyValidators int `json:"head_timely_validators"`
|
||||
SourceTimelyValidators int `json:"source_timely_validators"`
|
||||
TargetCorrectValidators int `json:"target_correct_validators"`
|
||||
TargetTimelyValidators int `json:"target_timely_validators"`
|
||||
NonParticipatingValidators []*nonParticipatingValidator `json:"nonparticipating_validators"`
|
||||
Blobs int `json:"blobs"`
|
||||
Epoch phase0.Epoch `json:"epoch"`
|
||||
FirstSlot phase0.Slot `json:"first_slot"`
|
||||
LastSlot phase0.Slot `json:"last_slot"`
|
||||
Blocks int `json:"blocks"`
|
||||
Proposals []*epochProposal `json:"proposals"`
|
||||
SyncCommitteeValidators int `json:"sync_committee_validators"`
|
||||
SyncCommittee []*epochSyncCommittee `json:"sync_committees"`
|
||||
ActiveValidators int `json:"active_validators"`
|
||||
ParticipatingValidators int `json:"participating_validators"`
|
||||
HeadCorrectValidators int `json:"head_correct_validators"`
|
||||
HeadTimelyValidators int `json:"head_timely_validators"`
|
||||
SourceTimelyValidators int `json:"source_timely_validators"`
|
||||
TargetCorrectValidators int `json:"target_correct_validators"`
|
||||
TargetTimelyValidators int `json:"target_timely_validators"`
|
||||
NonParticipatingValidators []*attestingValidator `json:"nonparticipating_validators"`
|
||||
NonHeadCorrectValidators []*attestingValidator `json:"nonheadcorrect_validators"`
|
||||
NonHeadTimelyValidators []*attestingValidator `json:"nonheadtimely_validators"`
|
||||
NonTargetCorrectValidators []*attestingValidator `json:"nontargetcorrect_validators"`
|
||||
NonSourceTimelyValidators []*attestingValidator `json:"nonsourcetimely_validators"`
|
||||
Blobs int `json:"blobs"`
|
||||
}
|
||||
|
||||
type epochProposal struct {
|
||||
Slot phase0.Slot `json:"slot"`
|
||||
Proposer phase0.ValidatorIndex `json:"proposer"`
|
||||
Block bool `json:"block"`
|
||||
ValidatorIndex phase0.ValidatorIndex `json:"validator_index"`
|
||||
Slot phase0.Slot `json:"slot"`
|
||||
Block bool `json:"block"`
|
||||
}
|
||||
|
||||
type epochSyncCommittee struct {
|
||||
Index phase0.ValidatorIndex `json:"index"`
|
||||
Missed int `json:"missed"`
|
||||
ValidatorIndex phase0.ValidatorIndex `json:"validator_index"`
|
||||
Missed int `json:"missed"`
|
||||
MissedSlots []phase0.Slot `json:"missed_slots"`
|
||||
}
|
||||
|
||||
type nonParticipatingValidator struct {
|
||||
Validator phase0.ValidatorIndex `json:"validator_index"`
|
||||
Slot phase0.Slot `json:"slot"`
|
||||
Committee phase0.CommitteeIndex `json:"committee_index"`
|
||||
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"`
|
||||
}
|
||||
|
||||
func newCommand(_ context.Context) (*command, error) {
|
||||
c := &command{
|
||||
quiet: viper.GetBool("quiet"),
|
||||
verbose: viper.GetBool("verbose"),
|
||||
debug: viper.GetBool("debug"),
|
||||
summary: &epochSummary{},
|
||||
quiet: viper.GetBool("quiet"),
|
||||
verbose: viper.GetBool("verbose"),
|
||||
debug: viper.GetBool("debug"),
|
||||
validatorsStr: viper.GetStringSlice("validators"),
|
||||
summary: &epochSummary{
|
||||
Proposals: make([]*epochProposal, 0),
|
||||
},
|
||||
validators: make(map[phase0.ValidatorIndex]struct{}),
|
||||
blocksCache: make(map[string]*spec.VersionedSignedBeaconBlock),
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ func (c *command) outputTxt(_ context.Context) (string, error) {
|
||||
missedProposals := make([]string, 0, len(c.summary.Proposals))
|
||||
for _, proposal := range c.summary.Proposals {
|
||||
if !proposal.Block {
|
||||
missedProposals = append(missedProposals, fmt.Sprintf("\n Slot %d (validator %d)", proposal.Slot, proposal.Proposer))
|
||||
missedProposals = append(missedProposals, fmt.Sprintf("\n Slot %d (validator %d)", proposal.Slot, proposal.ValidatorIndex))
|
||||
} else {
|
||||
proposedBlocks++
|
||||
}
|
||||
@@ -64,7 +64,7 @@ func (c *command) outputTxt(_ context.Context) (string, error) {
|
||||
builder.WriteString("\n Slot ")
|
||||
builder.WriteString(fmt.Sprintf("%d (%d/%d)", proposal.Slot, uint64(proposal.Slot)%uint64(len(c.summary.Proposals)), len(c.summary.Proposals)))
|
||||
builder.WriteString(" validator ")
|
||||
builder.WriteString(fmt.Sprintf("%d", proposal.Proposer))
|
||||
builder.WriteString(fmt.Sprintf("%d", proposal.ValidatorIndex))
|
||||
builder.WriteString(" not proposed or not included")
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ func (c *command) outputTxt(_ context.Context) (string, error) {
|
||||
if c.verbose {
|
||||
for _, syncCommittee := range c.summary.SyncCommittee {
|
||||
builder.WriteString("\n Validator ")
|
||||
builder.WriteString(fmt.Sprintf("%d", syncCommittee.Index))
|
||||
builder.WriteString(fmt.Sprintf("%d", syncCommittee.ValidatorIndex))
|
||||
builder.WriteString(" included ")
|
||||
builder.WriteString(fmt.Sprintf("%d/%d", proposedBlocks-syncCommittee.Missed, proposedBlocks))
|
||||
builder.WriteString(fmt.Sprintf(" (%0.2f%%)", 100.0*float64(proposedBlocks-syncCommittee.Missed)/float64(proposedBlocks)))
|
||||
|
||||
@@ -43,6 +43,14 @@ func (c *command) process(ctx context.Context) error {
|
||||
c.summary.FirstSlot = c.chainTime.FirstSlotOfEpoch(c.summary.Epoch)
|
||||
c.summary.LastSlot = c.chainTime.FirstSlotOfEpoch(c.summary.Epoch+1) - 1
|
||||
|
||||
validators, err := util.ParseValidators(ctx, c.validatorsProvider, c.validatorsStr, "head")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to parse validators")
|
||||
}
|
||||
for _, validator := range validators {
|
||||
c.validators[validator.Index] = struct{}{}
|
||||
}
|
||||
|
||||
if err := c.processProposerDuties(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -52,6 +60,7 @@ func (c *command) process(ctx context.Context) error {
|
||||
if err := c.processSyncCommitteeDuties(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.processBlobs(ctx)
|
||||
}
|
||||
|
||||
@@ -69,10 +78,20 @@ func (c *command) processProposerDuties(ctx context.Context) error {
|
||||
return errors.Wrap(err, fmt.Sprintf("failed to obtain block for slot %d", duty.Slot))
|
||||
}
|
||||
present := block != nil
|
||||
if present {
|
||||
c.summary.Blocks++
|
||||
}
|
||||
|
||||
_, exists := c.validators[duty.ValidatorIndex]
|
||||
if len(c.validators) > 0 && !exists {
|
||||
// Not one of ours.
|
||||
continue
|
||||
}
|
||||
|
||||
c.summary.Proposals = append(c.summary.Proposals, &epochProposal{
|
||||
Slot: duty.Slot,
|
||||
Proposer: duty.ValidatorIndex,
|
||||
Block: present,
|
||||
Slot: duty.Slot,
|
||||
ValidatorIndex: duty.ValidatorIndex,
|
||||
Block: present,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -80,14 +99,25 @@ func (c *command) processProposerDuties(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (c *command) activeValidators(ctx context.Context) (map[phase0.ValidatorIndex]*apiv1.Validator, error) {
|
||||
validatorIndices := make([]phase0.ValidatorIndex, 0, len(c.validators))
|
||||
for validator := range c.validators {
|
||||
validatorIndices = append(validatorIndices, validator)
|
||||
}
|
||||
|
||||
response, err := c.validatorsProvider.Validators(ctx, &api.ValidatorsOpts{
|
||||
State: fmt.Sprintf("%d", c.chainTime.FirstSlotOfEpoch(c.summary.Epoch)),
|
||||
State: "head",
|
||||
Indices: validatorIndices,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to obtain validators for epoch")
|
||||
}
|
||||
activeValidators := make(map[phase0.ValidatorIndex]*apiv1.Validator)
|
||||
for _, validator := range response.Data {
|
||||
_, exists := c.validators[validator.Index]
|
||||
if len(c.validators) > 0 && !exists {
|
||||
continue
|
||||
}
|
||||
|
||||
if validator.Validator.ActivationEpoch <= c.summary.Epoch && validator.Validator.ExitEpoch > c.summary.Epoch {
|
||||
activeValidators[validator.Index] = validator
|
||||
}
|
||||
@@ -112,20 +142,45 @@ func (c *command) processAttesterDuties(ctx context.Context) error {
|
||||
lastSlot = c.chainTime.CurrentSlot()
|
||||
}
|
||||
|
||||
var votes map[phase0.ValidatorIndex]struct{}
|
||||
var participations map[phase0.ValidatorIndex]*nonParticipatingValidator
|
||||
c.summary.ParticipatingValidators, c.summary.HeadCorrectValidators, c.summary.HeadTimelyValidators, c.summary.SourceTimelyValidators, c.summary.TargetCorrectValidators, c.summary.TargetTimelyValidators, votes, participations, err = c.processSlots(ctx, firstSlot, lastSlot)
|
||||
participatingValidators, headCorrectValidators, headTimelyValidators, sourceTimelyValidators, targetCorrectValidators, targetTimelyValidators, participations, err := c.processSlots(ctx, firstSlot, lastSlot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.summary.NonParticipatingValidators = make([]*nonParticipatingValidator, 0, len(activeValidators)-len(votes))
|
||||
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.NonParticipatingValidators = make([]*attestingValidator, 0, len(activeValidators)-len(participatingValidators))
|
||||
for activeValidatorIndex := range activeValidators {
|
||||
if _, exists := votes[activeValidatorIndex]; !exists {
|
||||
if _, exists := participatingValidators[activeValidatorIndex]; !exists {
|
||||
if _, exists := participations[activeValidatorIndex]; exists {
|
||||
c.summary.NonParticipatingValidators = append(c.summary.NonParticipatingValidators, participations[activeValidatorIndex])
|
||||
}
|
||||
}
|
||||
if _, exists := headCorrectValidators[activeValidatorIndex]; !exists {
|
||||
if _, exists := participations[activeValidatorIndex]; exists {
|
||||
c.summary.NonHeadCorrectValidators = append(c.summary.NonHeadCorrectValidators, participations[activeValidatorIndex])
|
||||
}
|
||||
}
|
||||
if _, exists := headTimelyValidators[activeValidatorIndex]; !exists {
|
||||
if _, exists := participations[activeValidatorIndex]; exists {
|
||||
c.summary.NonHeadTimelyValidators = append(c.summary.NonHeadTimelyValidators, participations[activeValidatorIndex])
|
||||
}
|
||||
}
|
||||
if _, exists := targetCorrectValidators[activeValidatorIndex]; !exists {
|
||||
if _, exists := participations[activeValidatorIndex]; exists {
|
||||
c.summary.NonTargetCorrectValidators = append(c.summary.NonTargetCorrectValidators, participations[activeValidatorIndex])
|
||||
}
|
||||
}
|
||||
if _, exists := sourceTimelyValidators[activeValidatorIndex]; !exists {
|
||||
if _, exists := participations[activeValidatorIndex]; exists {
|
||||
c.summary.NonSourceTimelyValidators = append(c.summary.NonSourceTimelyValidators, participations[activeValidatorIndex])
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Slice(c.summary.NonParticipatingValidators, func(i int, j int) bool {
|
||||
if c.summary.NonParticipatingValidators[i].Slot != c.summary.NonParticipatingValidators[j].Slot {
|
||||
@@ -140,18 +195,18 @@ func (c *command) processAttesterDuties(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func (c *command) processSlots(ctx context.Context,
|
||||
firstSlot phase0.Slot,
|
||||
lastSlot phase0.Slot,
|
||||
) (
|
||||
int,
|
||||
int,
|
||||
int,
|
||||
int,
|
||||
int,
|
||||
int,
|
||||
map[phase0.ValidatorIndex]struct{},
|
||||
map[phase0.ValidatorIndex]*nonParticipatingValidator,
|
||||
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{})
|
||||
@@ -161,7 +216,7 @@ func (c *command) processSlots(ctx context.Context,
|
||||
targetCorrects := make(map[phase0.ValidatorIndex]struct{})
|
||||
targetTimelys := make(map[phase0.ValidatorIndex]struct{})
|
||||
allCommittees := make(map[phase0.Slot]map[phase0.CommitteeIndex][]phase0.ValidatorIndex)
|
||||
participations := make(map[phase0.ValidatorIndex]*nonParticipatingValidator)
|
||||
participations := make(map[phase0.ValidatorIndex]*attestingValidator)
|
||||
|
||||
// Need a cache of beacon block headers to reduce lookup times.
|
||||
headersCache := util.NewBeaconBlockHeaderCache(c.beaconBlockHeadersProvider)
|
||||
@@ -169,7 +224,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 0, 0, 0, 0, 0, 0, nil, nil, errors.Wrap(err, fmt.Sprintf("failed to obtain block for slot %d", slot))
|
||||
return nil, nil, nil, nil, nil, nil, nil, errors.Wrap(err, fmt.Sprintf("failed to obtain block for slot %d", slot))
|
||||
}
|
||||
if block == nil {
|
||||
// No block at this slot; that's fine.
|
||||
@@ -177,11 +232,11 @@ func (c *command) processSlots(ctx context.Context,
|
||||
}
|
||||
slot, err := block.Slot()
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, 0, 0, nil, nil, err
|
||||
return nil, nil, nil, nil, nil, nil, nil, err
|
||||
}
|
||||
attestations, err := block.Attestations()
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, 0, 0, nil, nil, err
|
||||
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) {
|
||||
@@ -194,18 +249,29 @@ func (c *command) processSlots(ctx context.Context,
|
||||
State: fmt.Sprintf("%d", attestation.Data.Slot),
|
||||
})
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, 0, 0, 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", attestation.Data.Slot))
|
||||
}
|
||||
for _, beaconCommittee := range response.Data {
|
||||
if _, exists := allCommittees[beaconCommittee.Slot]; !exists {
|
||||
allCommittees[beaconCommittee.Slot] = make(map[phase0.CommitteeIndex][]phase0.ValidatorIndex)
|
||||
}
|
||||
|
||||
allCommittees[beaconCommittee.Slot][beaconCommittee.Index] = beaconCommittee.Validators
|
||||
|
||||
for _, index := range beaconCommittee.Validators {
|
||||
participations[index] = &nonParticipatingValidator{
|
||||
Validator: index,
|
||||
Slot: beaconCommittee.Slot,
|
||||
Committee: beaconCommittee.Index,
|
||||
if len(c.validators) > 0 {
|
||||
if _, exists := c.validators[index]; !exists {
|
||||
// Not one of our validators.
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if _, exists := participations[index]; !exists {
|
||||
participations[index] = &attestingValidator{
|
||||
Validator: index,
|
||||
Slot: beaconCommittee.Slot,
|
||||
Committee: beaconCommittee.Index,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,44 +280,70 @@ func (c *command) processSlots(ctx context.Context,
|
||||
committee := slotCommittees[attestation.Data.Index]
|
||||
|
||||
inclusionDistance := slot - attestation.Data.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 0, 0, 0, 0, 0, 0, nil, nil, err
|
||||
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 0, 0, 0, 0, 0, 0, nil, nil, err
|
||||
return nil, nil, nil, nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
for i := uint64(0); i < attestation.AggregationBits.Len(); i++ {
|
||||
if attestation.AggregationBits.BitAt(i) {
|
||||
votes[committee[int(i)]] = struct{}{}
|
||||
if _, exists := headCorrects[committee[int(i)]]; !exists && headCorrect {
|
||||
headCorrects[committee[int(i)]] = struct{}{}
|
||||
validatorIndex := committee[int(i)]
|
||||
if len(c.validators) > 0 {
|
||||
if _, exists := c.validators[validatorIndex]; !exists {
|
||||
// Not one of our validators.
|
||||
continue
|
||||
}
|
||||
}
|
||||
if _, exists := headTimelys[committee[int(i)]]; !exists && headCorrect && inclusionDistance == 1 {
|
||||
headTimelys[committee[int(i)]] = struct{}{}
|
||||
|
||||
// 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].Head = &head
|
||||
participations[validatorIndex].TargetVote = &attestation.Data.Target.Root
|
||||
participations[validatorIndex].Target = &target
|
||||
participations[validatorIndex].InclusionSlot = slot
|
||||
}
|
||||
if _, exists := sourceTimelys[committee[int(i)]]; !exists && inclusionDistance <= 5 {
|
||||
sourceTimelys[committee[int(i)]] = struct{}{}
|
||||
|
||||
votes[validatorIndex] = struct{}{}
|
||||
if _, exists := headCorrects[validatorIndex]; !exists && headCorrect {
|
||||
headCorrects[validatorIndex] = struct{}{}
|
||||
}
|
||||
if _, exists := targetCorrects[committee[int(i)]]; !exists && targetCorrect {
|
||||
targetCorrects[committee[int(i)]] = struct{}{}
|
||||
if _, exists := headTimelys[validatorIndex]; !exists && headCorrect && inclusionDistance == 1 {
|
||||
headTimelys[validatorIndex] = struct{}{}
|
||||
}
|
||||
if _, exists := targetTimelys[committee[int(i)]]; !exists && targetCorrect && inclusionDistance <= 32 {
|
||||
targetTimelys[committee[int(i)]] = 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 len(votes),
|
||||
len(headCorrects),
|
||||
len(headTimelys),
|
||||
len(sourceTimelys),
|
||||
len(targetCorrects),
|
||||
len(targetTimelys),
|
||||
votes,
|
||||
|
||||
return votes,
|
||||
headCorrects,
|
||||
headTimelys,
|
||||
sourceTimelys,
|
||||
targetCorrects,
|
||||
targetTimelys,
|
||||
participations,
|
||||
nil
|
||||
}
|
||||
@@ -273,7 +365,18 @@ func (c *command) processSyncCommitteeDuties(ctx context.Context) error {
|
||||
return errors.Wrap(err, "empty sync committee")
|
||||
}
|
||||
|
||||
for _, validatorIndex := range committee.Validators {
|
||||
if len(c.validators) == 0 {
|
||||
c.summary.SyncCommitteeValidators++
|
||||
} else {
|
||||
if _, exists := c.validators[validatorIndex]; exists {
|
||||
c.summary.SyncCommitteeValidators++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
missed := make(map[phase0.ValidatorIndex]int)
|
||||
missedSlots := make(map[phase0.ValidatorIndex][]phase0.Slot)
|
||||
for _, index := range committee.Validators {
|
||||
missed[index] = 0
|
||||
}
|
||||
@@ -297,8 +400,14 @@ func (c *command) processSyncCommitteeDuties(ctx context.Context) error {
|
||||
return errors.Wrapf(err, "failed to obtain sync aggregate for slot %d", slot)
|
||||
}
|
||||
for i := uint64(0); i < aggregate.SyncCommitteeBits.Len(); i++ {
|
||||
validatorIndex := committee.Validators[int(i)]
|
||||
if _, exists := c.validators[validatorIndex]; !exists {
|
||||
// Not one of ours.
|
||||
continue
|
||||
}
|
||||
if !aggregate.SyncCommitteeBits.BitAt(i) {
|
||||
missed[committee.Validators[int(i)]]++
|
||||
missed[validatorIndex]++
|
||||
missedSlots[validatorIndex] = append(missedSlots[validatorIndex], slot)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -307,8 +416,9 @@ func (c *command) processSyncCommitteeDuties(ctx context.Context) error {
|
||||
for index, count := range missed {
|
||||
if count > 0 {
|
||||
c.summary.SyncCommittee = append(c.summary.SyncCommittee, &epochSyncCommittee{
|
||||
Index: index,
|
||||
Missed: count,
|
||||
ValidatorIndex: index,
|
||||
Missed: count,
|
||||
MissedSlots: missedSlots[index],
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -320,7 +430,7 @@ func (c *command) processSyncCommitteeDuties(ctx context.Context) error {
|
||||
return missedDiff > 0
|
||||
}
|
||||
// Then order by validator index.
|
||||
return c.summary.SyncCommittee[i].Index < c.summary.SyncCommittee[j].Index
|
||||
return c.summary.SyncCommittee[i].ValidatorIndex < c.summary.SyncCommittee[j].ValidatorIndex
|
||||
})
|
||||
|
||||
return nil
|
||||
@@ -378,10 +488,10 @@ func (c *command) setup(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (c *command) processBlobs(ctx context.Context) error {
|
||||
for slot := c.summary.FirstSlot; slot <= c.summary.LastSlot; slot++ {
|
||||
block, err := c.fetchBlock(ctx, fmt.Sprintf("%d", slot))
|
||||
for _, proposal := range c.summary.Proposals {
|
||||
block, err := c.fetchBlock(ctx, fmt.Sprintf("%d", proposal.Slot))
|
||||
if err != nil {
|
||||
return 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", proposal.Slot))
|
||||
}
|
||||
if block == nil {
|
||||
continue
|
||||
|
||||
@@ -46,9 +46,17 @@ In quiet mode this will return 0 if information for the epoch is found, otherwis
|
||||
|
||||
func init() {
|
||||
epochCmd.AddCommand(epochSummaryCmd)
|
||||
epochFlags(epochSummaryCmd)
|
||||
epochSummaryFlags(epochSummaryCmd)
|
||||
}
|
||||
|
||||
func epochSummaryFlags(cmd *cobra.Command) {
|
||||
epochFlags(cmd)
|
||||
cmd.Flags().StringSlice("validators", nil, "the validators for which to obtain a summary")
|
||||
}
|
||||
|
||||
func epochSummaryBindings(cmd *cobra.Command) {
|
||||
epochBindings(cmd)
|
||||
if err := viper.BindPFlag("validators", cmd.Flags().Lookup("validators")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,35 @@ import (
|
||||
"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,
|
||||
) (
|
||||
phase0.Root,
|
||||
error,
|
||||
) {
|
||||
slot := attestation.Data.Slot
|
||||
for {
|
||||
header, err := headersCache.Fetch(ctx, slot)
|
||||
if err != nil {
|
||||
return phase0.Root{}, err
|
||||
}
|
||||
if header == nil {
|
||||
// No block.
|
||||
slot--
|
||||
continue
|
||||
}
|
||||
if !header.Canonical {
|
||||
// Not canonical.
|
||||
slot--
|
||||
continue
|
||||
}
|
||||
|
||||
return header.Root, nil
|
||||
}
|
||||
}
|
||||
|
||||
// AttestationHeadCorrect returns true if the given attestation had the correct head.
|
||||
func AttestationHeadCorrect(ctx context.Context,
|
||||
headersCache *BeaconBlockHeaderCache,
|
||||
@@ -49,6 +78,37 @@ func AttestationHeadCorrect(ctx context.Context,
|
||||
}
|
||||
}
|
||||
|
||||
// AttestationTarget returns the target for which the attestation should have voted.
|
||||
func AttestationTarget(ctx context.Context,
|
||||
headersCache *BeaconBlockHeaderCache,
|
||||
chainTime chaintime.Service,
|
||||
attestation *phase0.Attestation,
|
||||
) (
|
||||
phase0.Root,
|
||||
error,
|
||||
) {
|
||||
// Start with first slot of the target epoch.
|
||||
slot := chainTime.FirstSlotOfEpoch(attestation.Data.Target.Epoch)
|
||||
for {
|
||||
header, err := headersCache.Fetch(ctx, slot)
|
||||
if err != nil {
|
||||
return phase0.Root{}, err
|
||||
}
|
||||
if header == nil {
|
||||
// No block.
|
||||
slot--
|
||||
continue
|
||||
}
|
||||
if !header.Canonical {
|
||||
// Not canonical.
|
||||
slot--
|
||||
continue
|
||||
}
|
||||
|
||||
return header.Root, nil
|
||||
}
|
||||
}
|
||||
|
||||
// AttestationTargetCorrect returns true if the given attestation had the correct target.
|
||||
func AttestationTargetCorrect(ctx context.Context,
|
||||
headersCache *BeaconBlockHeaderCache,
|
||||
|
||||
Reference in New Issue
Block a user