mirror of
https://github.com/wealdtech/ethdo.git
synced 2026-01-08 21:48:05 -05:00
Merge pull request #154 from wealdtech/electra_updates
Update to use electra version of go-eth2-client
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -15,6 +15,12 @@ coverage.html
|
|||||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||||
.glide/
|
.glide/
|
||||||
|
|
||||||
|
# Intellij
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Makefile
|
||||||
|
Makefile
|
||||||
|
|
||||||
# Vim
|
# Vim
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
electra:
|
||||||
|
- update to handle versioned attestations from go-eth2-client electra branch
|
||||||
|
|
||||||
1.36.2:
|
1.36.2:
|
||||||
- avoid crash when signing and verifing signatures using keys rather than accounts
|
- avoid crash when signing and verifing signatures using keys rather than accounts
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.22-bookworm as builder
|
FROM golang:1.23-bookworm AS builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/attestantio/go-eth2-client/spec"
|
||||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
@@ -27,7 +28,7 @@ type dataOut struct {
|
|||||||
debug bool
|
debug bool
|
||||||
quiet bool
|
quiet bool
|
||||||
verbose bool
|
verbose bool
|
||||||
attestation *phase0.Attestation
|
attestation *spec.VersionedAttestation
|
||||||
slot phase0.Slot
|
slot phase0.Slot
|
||||||
attestationIndex uint64
|
attestationIndex uint64
|
||||||
inclusionDelay phase0.Slot
|
inclusionDelay phase0.Slot
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
eth2client "github.com/attestantio/go-eth2-client"
|
eth2client "github.com/attestantio/go-eth2-client"
|
||||||
"github.com/attestantio/go-eth2-client/api"
|
"github.com/attestantio/go-eth2-client/api"
|
||||||
apiv1 "github.com/attestantio/go-eth2-client/api/v1"
|
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/attestantio/go-eth2-client/spec/phase0"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
standardchaintime "github.com/wealdtech/ethdo/services/chaintime/standard"
|
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")
|
return nil, errors.Wrap(err, "failed to obtain block attestations")
|
||||||
}
|
}
|
||||||
for i, attestation := range attestations {
|
for i, attestation := range attestations {
|
||||||
if attestation.Data.Slot == duty.Slot &&
|
attestationData, err := attestation.Data()
|
||||||
attestation.Data.Index == duty.CommitteeIndex &&
|
if err != nil {
|
||||||
attestation.AggregationBits.BitAt(duty.ValidatorCommitteeIndex) {
|
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
|
headCorrect := false
|
||||||
targetCorrect := false
|
targetCorrect := false
|
||||||
if data.verbose {
|
if data.verbose {
|
||||||
@@ -128,8 +137,12 @@ func process(ctx context.Context, data *dataIn) (*dataOut, error) {
|
|||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func calcHeadCorrect(ctx context.Context, data *dataIn, attestation *phase0.Attestation) (bool, error) {
|
func calcHeadCorrect(ctx context.Context, data *dataIn, 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 {
|
for {
|
||||||
response, err := data.eth2Client.(eth2client.BeaconBlockHeadersProvider).BeaconBlockHeader(ctx, &api.BeaconBlockHeaderOpts{
|
response, err := data.eth2Client.(eth2client.BeaconBlockHeadersProvider).BeaconBlockHeader(ctx, &api.BeaconBlockHeaderOpts{
|
||||||
Block: fmt.Sprintf("%d", slot),
|
Block: fmt.Sprintf("%d", slot),
|
||||||
@@ -149,13 +162,17 @@ func calcHeadCorrect(ctx context.Context, data *dataIn, attestation *phase0.Atte
|
|||||||
slot--
|
slot--
|
||||||
continue
|
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.
|
// Start with first slot of the target epoch.
|
||||||
slot := data.chainTime.FirstSlotOfEpoch(attestation.Data.Target.Epoch)
|
slot := data.chainTime.FirstSlotOfEpoch(attestationData.Target.Epoch)
|
||||||
for {
|
for {
|
||||||
response, err := data.eth2Client.(eth2client.BeaconBlockHeadersProvider).BeaconBlockHeader(ctx, &api.BeaconBlockHeaderOpts{
|
response, err := data.eth2Client.(eth2client.BeaconBlockHeadersProvider).BeaconBlockHeader(ctx, &api.BeaconBlockHeaderOpts{
|
||||||
Block: fmt.Sprintf("%d", slot),
|
Block: fmt.Sprintf("%d", slot),
|
||||||
@@ -175,7 +192,7 @@ func calcTargetCorrect(ctx context.Context, data *dataIn, attestation *phase0.At
|
|||||||
slot--
|
slot--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return bytes.Equal(response.Data.Root[:], attestation.Data.Target.Root[:]), nil
|
return bytes.Equal(response.Data.Root[:], attestationData.Target.Root[:]), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ type command struct {
|
|||||||
weightDenominator uint64
|
weightDenominator uint64
|
||||||
|
|
||||||
// Processing.
|
// Processing.
|
||||||
priorAttestations map[string]*attestationData
|
priorAttestations map[string]*attestationDataInfo
|
||||||
// Head roots provides the root of the head slot at given slots.
|
// Head roots provides the root of the head slot at given slots.
|
||||||
headRoots map[phase0.Slot]phase0.Root
|
headRoots map[phase0.Slot]phase0.Root
|
||||||
// Target roots provides the root of the target epoch at given slots.
|
// Target roots provides the root of the target epoch at given slots.
|
||||||
@@ -77,20 +77,20 @@ type blockAnalysis struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type attestationAnalysis struct {
|
type attestationAnalysis struct {
|
||||||
Head phase0.Root `json:"head"`
|
Head phase0.Root `json:"head"`
|
||||||
Target phase0.Root `json:"target"`
|
Target phase0.Root `json:"target"`
|
||||||
Distance int `json:"distance"`
|
Distance int `json:"distance"`
|
||||||
Duplicate *attestationData `json:"duplicate,omitempty"`
|
Duplicate *attestationDataInfo `json:"duplicate,omitempty"`
|
||||||
NewVotes int `json:"new_votes"`
|
NewVotes int `json:"new_votes"`
|
||||||
Votes int `json:"votes"`
|
Votes int `json:"votes"`
|
||||||
PossibleVotes int `json:"possible_votes"`
|
PossibleVotes int `json:"possible_votes"`
|
||||||
HeadCorrect bool `json:"head_correct"`
|
HeadCorrect bool `json:"head_correct"`
|
||||||
HeadTimely bool `json:"head_timely"`
|
HeadTimely bool `json:"head_timely"`
|
||||||
SourceTimely bool `json:"source_timely"`
|
SourceTimely bool `json:"source_timely"`
|
||||||
TargetCorrect bool `json:"target_correct"`
|
TargetCorrect bool `json:"target_correct"`
|
||||||
TargetTimely bool `json:"target_timely"`
|
TargetTimely bool `json:"target_timely"`
|
||||||
Score float64 `json:"score"`
|
Score float64 `json:"score"`
|
||||||
Value float64 `json:"value"`
|
Value float64 `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type syncCommitteeAnalysis struct {
|
type syncCommitteeAnalysis struct {
|
||||||
@@ -100,7 +100,7 @@ type syncCommitteeAnalysis struct {
|
|||||||
Value float64 `json:"value"`
|
Value float64 `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type attestationData struct {
|
type attestationDataInfo struct {
|
||||||
Block phase0.Slot `json:"block"`
|
Block phase0.Slot `json:"block"`
|
||||||
Index int `json:"index"`
|
Index int `json:"index"`
|
||||||
}
|
}
|
||||||
@@ -110,7 +110,7 @@ func newCommand(_ context.Context) (*command, error) {
|
|||||||
quiet: viper.GetBool("quiet"),
|
quiet: viper.GetBool("quiet"),
|
||||||
verbose: viper.GetBool("verbose"),
|
verbose: viper.GetBool("verbose"),
|
||||||
debug: viper.GetBool("debug"),
|
debug: viper.GetBool("debug"),
|
||||||
priorAttestations: make(map[string]*attestationData),
|
priorAttestations: make(map[string]*attestationDataInfo),
|
||||||
headRoots: make(map[phase0.Slot]phase0.Root),
|
headRoots: make(map[phase0.Slot]phase0.Root),
|
||||||
targetRoots: make(map[phase0.Slot]phase0.Root),
|
targetRoots: make(map[phase0.Slot]phase0.Root),
|
||||||
votes: make(map[phase0.Slot]map[phase0.CommitteeIndex]bitfield.Bitlist),
|
votes: make(map[phase0.Slot]map[phase0.CommitteeIndex]bitfield.Bitlist),
|
||||||
|
|||||||
@@ -34,20 +34,20 @@ func (c *command) output(ctx context.Context) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type attestationAnalysisJSON struct {
|
type attestationAnalysisJSON struct {
|
||||||
Head string `json:"head"`
|
Head string `json:"head"`
|
||||||
Target string `json:"target"`
|
Target string `json:"target"`
|
||||||
Distance int `json:"distance"`
|
Distance int `json:"distance"`
|
||||||
Duplicate *attestationData `json:"duplicate,omitempty"`
|
Duplicate *attestationDataInfo `json:"duplicate,omitempty"`
|
||||||
NewVotes int `json:"new_votes"`
|
NewVotes int `json:"new_votes"`
|
||||||
Votes int `json:"votes"`
|
Votes int `json:"votes"`
|
||||||
PossibleVotes int `json:"possible_votes"`
|
PossibleVotes int `json:"possible_votes"`
|
||||||
HeadCorrect bool `json:"head_correct"`
|
HeadCorrect bool `json:"head_correct"`
|
||||||
HeadTimely bool `json:"head_timely"`
|
HeadTimely bool `json:"head_timely"`
|
||||||
SourceTimely bool `json:"source_timely"`
|
SourceTimely bool `json:"source_timely"`
|
||||||
TargetCorrect bool `json:"target_correct"`
|
TargetCorrect bool `json:"target_correct"`
|
||||||
TargetTimely bool `json:"target_timely"`
|
TargetTimely bool `json:"target_timely"`
|
||||||
Score float64 `json:"score"`
|
Score float64 `json:"score"`
|
||||||
Value float64 `json:"value"`
|
Value float64 `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *attestationAnalysis) MarshalJSON() ([]byte, error) {
|
func (a *attestationAnalysis) MarshalJSON() ([]byte, error) {
|
||||||
|
|||||||
@@ -63,8 +63,12 @@ func (c *command) process(ctx context.Context) error {
|
|||||||
// Calculate how many parents we need to fetch.
|
// Calculate how many parents we need to fetch.
|
||||||
minSlot := slot
|
minSlot := slot
|
||||||
for _, attestation := range attestations {
|
for _, attestation := range attestations {
|
||||||
if attestation.Data.Slot < minSlot {
|
attestationData, err := attestation.Data()
|
||||||
minSlot = attestation.Data.Slot
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to obtain attestation data")
|
||||||
|
}
|
||||||
|
if attestationData.Slot < minSlot {
|
||||||
|
minSlot = attestationData.Slot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.debug {
|
if c.debug {
|
||||||
@@ -103,10 +107,14 @@ func (c *command) analyzeAttestations(ctx context.Context, block *spec.Versioned
|
|||||||
if c.debug {
|
if c.debug {
|
||||||
fmt.Printf("Processing attestation %d\n", i)
|
fmt.Printf("Processing attestation %d\n", i)
|
||||||
}
|
}
|
||||||
|
attestationData, err := attestation.Data()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to obtain attestation data")
|
||||||
|
}
|
||||||
analysis := &attestationAnalysis{
|
analysis := &attestationAnalysis{
|
||||||
Head: attestation.Data.BeaconBlockRoot,
|
Head: attestationData.BeaconBlockRoot,
|
||||||
Target: attestation.Data.Target.Root,
|
Target: attestationData.Target.Root,
|
||||||
Distance: int(slot - attestation.Data.Slot),
|
Distance: int(slot - attestationData.Slot),
|
||||||
}
|
}
|
||||||
|
|
||||||
root, err := attestation.HashTreeRoot()
|
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 {
|
if info, exists := c.priorAttestations[fmt.Sprintf("%#x", root)]; exists {
|
||||||
analysis.Duplicate = info
|
analysis.Duplicate = info
|
||||||
} else {
|
} else {
|
||||||
data := attestation.Data
|
aggregationBits, err := attestation.AggregationBits()
|
||||||
_, exists := blockVotes[data.Slot]
|
if err != nil {
|
||||||
if !exists {
|
return err
|
||||||
blockVotes[data.Slot] = make(map[phase0.CommitteeIndex]bitfield.Bitlist)
|
|
||||||
}
|
}
|
||||||
_, exists = blockVotes[data.Slot][data.Index]
|
_, exists := blockVotes[attestationData.Slot]
|
||||||
if !exists {
|
if !exists {
|
||||||
blockVotes[data.Slot][data.Index] = bitfield.NewBitlist(attestation.AggregationBits.Len())
|
blockVotes[attestationData.Slot] = make(map[phase0.CommitteeIndex]bitfield.Bitlist)
|
||||||
|
}
|
||||||
|
_, exists = blockVotes[attestationData.Slot][attestationData.Index]
|
||||||
|
if !exists {
|
||||||
|
blockVotes[attestationData.Slot][attestationData.Index] = bitfield.NewBitlist(aggregationBits.Len())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count new votes.
|
// Count new votes.
|
||||||
analysis.PossibleVotes = int(attestation.AggregationBits.Len())
|
analysis.PossibleVotes = int(aggregationBits.Len())
|
||||||
for j := range attestation.AggregationBits.Len() {
|
for j := range aggregationBits.Len() {
|
||||||
if attestation.AggregationBits.BitAt(j) {
|
if aggregationBits.BitAt(j) {
|
||||||
analysis.Votes++
|
analysis.Votes++
|
||||||
if blockVotes[data.Slot][data.Index].BitAt(j) {
|
if blockVotes[attestationData.Slot][attestationData.Index].BitAt(j) {
|
||||||
// Already attested to in this block; skip.
|
// Already attested to in this block; skip.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if c.votes[data.Slot][data.Index].BitAt(j) {
|
if c.votes[attestationData.Slot][attestationData.Index].BitAt(j) {
|
||||||
// Already attested to in a previous block; skip.
|
// Already attested to in a previous block; skip.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
analysis.NewVotes++
|
analysis.NewVotes++
|
||||||
blockVotes[data.Slot][data.Index].SetBitAt(j, true)
|
blockVotes[attestationData.Slot][attestationData.Index].SetBitAt(j, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Calculate head correct.
|
// Calculate head correct.
|
||||||
var err error
|
|
||||||
analysis.HeadCorrect, err = c.calcHeadCorrect(ctx, attestation)
|
analysis.HeadCorrect, err = c.calcHeadCorrect(ctx, attestation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate head timely.
|
// Calculate head timely.
|
||||||
analysis.HeadTimely = analysis.HeadCorrect && attestation.Data.Slot == slot-1
|
analysis.HeadTimely = analysis.HeadCorrect && attestationData.Slot == slot-1
|
||||||
|
|
||||||
// Calculate source timely.
|
// Calculate source timely.
|
||||||
analysis.SourceTimely = attestation.Data.Slot >= slot-5
|
analysis.SourceTimely = attestationData.Slot >= slot-5
|
||||||
|
|
||||||
// Calculate target correct.
|
// Calculate target correct.
|
||||||
analysis.TargetCorrect, err = c.calcTargetCorrect(ctx, attestation)
|
analysis.TargetCorrect, err = c.calcTargetCorrect(ctx, attestation)
|
||||||
@@ -164,7 +174,7 @@ func (c *command) analyzeAttestations(ctx context.Context, block *spec.Versioned
|
|||||||
|
|
||||||
// Calculate target timely.
|
// Calculate target timely.
|
||||||
if block.Version < spec.DataVersionDeneb {
|
if block.Version < spec.DataVersionDeneb {
|
||||||
analysis.TargetTimely = attestation.Data.Slot >= slot-32
|
analysis.TargetTimely = attestationData.Slot >= slot-32
|
||||||
} else {
|
} else {
|
||||||
analysis.TargetTimely = true
|
analysis.TargetTimely = true
|
||||||
}
|
}
|
||||||
@@ -255,22 +265,29 @@ func (c *command) processParentBlock(_ context.Context, block *spec.VersionedSig
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.priorAttestations[fmt.Sprintf("%#x", root)] = &attestationData{
|
c.priorAttestations[fmt.Sprintf("%#x", root)] = &attestationDataInfo{
|
||||||
Block: slot,
|
Block: slot,
|
||||||
Index: i,
|
Index: i,
|
||||||
}
|
}
|
||||||
|
|
||||||
data := attestation.Data
|
data, err := attestation.Data()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
_, exists := c.votes[data.Slot]
|
_, exists := c.votes[data.Slot]
|
||||||
if !exists {
|
if !exists {
|
||||||
c.votes[data.Slot] = make(map[phase0.CommitteeIndex]bitfield.Bitlist)
|
c.votes[data.Slot] = make(map[phase0.CommitteeIndex]bitfield.Bitlist)
|
||||||
}
|
}
|
||||||
_, exists = c.votes[data.Slot][data.Index]
|
_, exists = c.votes[data.Slot][data.Index]
|
||||||
if !exists {
|
aggregationBits, err := attestation.AggregationBits()
|
||||||
c.votes[data.Slot][data.Index] = bitfield.NewBitlist(attestation.AggregationBits.Len())
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
for j := range attestation.AggregationBits.Len() {
|
if !exists {
|
||||||
if attestation.AggregationBits.BitAt(j) {
|
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)
|
c.votes[data.Slot][data.Index].SetBitAt(j, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -385,8 +402,12 @@ func (c *command) setup(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *command) calcHeadCorrect(ctx context.Context, attestation *phase0.Attestation) (bool, error) {
|
func (c *command) calcHeadCorrect(ctx context.Context, 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
|
||||||
root, exists := c.headRoots[slot]
|
root, exists := c.headRoots[slot]
|
||||||
if !exists {
|
if !exists {
|
||||||
for {
|
for {
|
||||||
@@ -413,20 +434,24 @@ func (c *command) calcHeadCorrect(ctx context.Context, attestation *phase0.Attes
|
|||||||
slot--
|
slot--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.headRoots[attestation.Data.Slot] = response.Data.Root
|
c.headRoots[attestationData.Slot] = response.Data.Root
|
||||||
root = response.Data.Root
|
root = response.Data.Root
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytes.Equal(root[:], attestation.Data.BeaconBlockRoot[:]), nil
|
return bytes.Equal(root[:], attestationData.BeaconBlockRoot[:]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *command) calcTargetCorrect(ctx context.Context, attestation *phase0.Attestation) (bool, error) {
|
func (c *command) calcTargetCorrect(ctx context.Context, attestation *spec.VersionedAttestation) (bool, error) {
|
||||||
root, exists := c.targetRoots[attestation.Data.Slot]
|
attestationData, err := attestation.Data()
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, "failed to obtain attestation data")
|
||||||
|
}
|
||||||
|
root, exists := c.targetRoots[attestationData.Slot]
|
||||||
if !exists {
|
if !exists {
|
||||||
// Start with first slot of the target epoch.
|
// Start with first slot of the target epoch.
|
||||||
slot := c.chainTime.FirstSlotOfEpoch(attestation.Data.Target.Epoch)
|
slot := c.chainTime.FirstSlotOfEpoch(attestationData.Target.Epoch)
|
||||||
for {
|
for {
|
||||||
response, err := c.blockHeadersProvider.BeaconBlockHeader(ctx, &api.BeaconBlockHeaderOpts{
|
response, err := c.blockHeadersProvider.BeaconBlockHeader(ctx, &api.BeaconBlockHeaderOpts{
|
||||||
Block: fmt.Sprintf("%d", slot),
|
Block: fmt.Sprintf("%d", slot),
|
||||||
@@ -450,12 +475,12 @@ func (c *command) calcTargetCorrect(ctx context.Context, attestation *phase0.Att
|
|||||||
slot--
|
slot--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.targetRoots[attestation.Data.Slot] = response.Data.Root
|
c.targetRoots[attestationData.Slot] = response.Data.Root
|
||||||
root = response.Data.Root
|
root = response.Data.Root
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bytes.Equal(root[:], attestation.Data.Target.Root[:]), nil
|
return bytes.Equal(root[:], attestationData.Target.Root[:]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *command) analyzeSyncCommittees(_ context.Context, block *spec.VersionedSignedBeaconBlock) error {
|
func (c *command) analyzeSyncCommittees(_ context.Context, block *spec.VersionedSignedBeaconBlock) error {
|
||||||
|
|||||||
@@ -239,17 +239,21 @@ func (c *command) processSlots(ctx context.Context,
|
|||||||
return nil, nil, nil, nil, nil, nil, nil, err
|
return nil, nil, nil, nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
for _, attestation := range attestations {
|
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.
|
// Outside of this epoch's range.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
slotCommittees, exists := allCommittees[attestation.Data.Slot]
|
slotCommittees, exists := allCommittees[attestationData.Slot]
|
||||||
if !exists {
|
if !exists {
|
||||||
response, err := c.beaconCommitteesProvider.BeaconCommittees(ctx, &api.BeaconCommitteesOpts{
|
response, err := c.beaconCommitteesProvider.BeaconCommittees(ctx, &api.BeaconCommitteesOpts{
|
||||||
State: fmt.Sprintf("%d", attestation.Data.Slot),
|
State: fmt.Sprintf("%d", attestationData.Slot),
|
||||||
})
|
})
|
||||||
if err != nil {
|
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 {
|
for _, beaconCommittee := range response.Data {
|
||||||
if _, exists := allCommittees[beaconCommittee.Slot]; !exists {
|
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)
|
head, err := util.AttestationHead(ctx, headersCache, attestation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -298,8 +302,12 @@ func (c *command) processSlots(ctx context.Context,
|
|||||||
return nil, nil, nil, nil, nil, nil, nil, err
|
return nil, nil, nil, nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range attestation.AggregationBits.Len() {
|
aggregationBits, err := attestation.AggregationBits()
|
||||||
if attestation.AggregationBits.BitAt(i) {
|
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)]
|
validatorIndex := committee[int(i)]
|
||||||
if len(c.validators) > 0 {
|
if len(c.validators) > 0 {
|
||||||
if _, exists := c.validators[validatorIndex]; !exists {
|
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.
|
// Only set the information from the first attestation we find for this validator.
|
||||||
if participations[validatorIndex].InclusionSlot == 0 {
|
if participations[validatorIndex].InclusionSlot == 0 {
|
||||||
participations[validatorIndex].HeadVote = &attestation.Data.BeaconBlockRoot
|
participations[validatorIndex].HeadVote = &attestationData.BeaconBlockRoot
|
||||||
participations[validatorIndex].Head = &head
|
participations[validatorIndex].Head = &head
|
||||||
participations[validatorIndex].TargetVote = &attestation.Data.Target.Root
|
participations[validatorIndex].TargetVote = &attestationData.Target.Root
|
||||||
participations[validatorIndex].Target = &target
|
participations[validatorIndex].Target = &target
|
||||||
participations[validatorIndex].InclusionSlot = slot
|
participations[validatorIndex].InclusionSlot = slot
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -243,16 +243,24 @@ func (c *command) processAttesterDutiesSlot(ctx context.Context,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, attestation := range attestations {
|
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.
|
// We do not have any attestations for this slot.
|
||||||
continue
|
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.
|
// We do not have any attestations for this committee.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, duty := range dutiesBySlot[attestation.Data.Slot][attestation.Data.Index] {
|
for _, duty := range dutiesBySlot[attestationData.Slot][attestationData.Index] {
|
||||||
if attestation.AggregationBits.BitAt(duty.ValidatorCommitteeIndex) {
|
aggregationBits, err := attestation.AggregationBits()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to obtain aggregation bits")
|
||||||
|
}
|
||||||
|
if aggregationBits.BitAt(duty.ValidatorCommitteeIndex) {
|
||||||
// Found it.
|
// Found it.
|
||||||
if _, exists := votes[duty.ValidatorIndex]; exists {
|
if _, exists := votes[duty.ValidatorIndex]; exists {
|
||||||
// Duplicate; ignore.
|
// Duplicate; ignore.
|
||||||
@@ -261,13 +269,13 @@ func (c *command) processAttesterDutiesSlot(ctx context.Context,
|
|||||||
votes[duty.ValidatorIndex] = struct{}{}
|
votes[duty.ValidatorIndex] = struct{}{}
|
||||||
|
|
||||||
// Update the metrics for the attestation.
|
// 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++
|
c.summary.Slots[index].Attestations.Included++
|
||||||
inclusionDelay := slot - duty.Slot
|
inclusionDelay := slot - duty.Slot
|
||||||
|
|
||||||
fault := &validatorFault{
|
fault := &validatorFault{
|
||||||
Validator: duty.ValidatorIndex,
|
Validator: duty.ValidatorIndex,
|
||||||
AttestationData: attestation.Data,
|
AttestationData: attestationData,
|
||||||
InclusionDistance: int(inclusionDelay),
|
InclusionDistance: int(inclusionDelay),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -5,7 +5,7 @@ go 1.22.7
|
|||||||
toolchain go1.23.2
|
toolchain go1.23.2
|
||||||
|
|
||||||
require (
|
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/ferranbt/fastssz v0.1.4
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible
|
github.com/gofrs/uuid v4.4.0+incompatible
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
|
|||||||
2
go.sum
2
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 h1:KmF9kPNNWWGfE7l1BP7pXps4EOXgKnYeFGR0/WbyFhY=
|
||||||
github.com/attestantio/go-eth2-client v0.22.0/go.mod h1:d7ZPNrMX8jLfIgML5u7QZxFo2AukLM+5m08iMaLdqb8=
|
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 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
|
||||||
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ func NewAttestationSubmitter() eth2client.AttestationsSubmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SubmitAttestations is a mock.
|
// SubmitAttestations is a mock.
|
||||||
func (m *AttestationsSubmitter) SubmitAttestations(_ context.Context, _ []*phase0.Attestation) error {
|
func (m *AttestationsSubmitter) SubmitAttestations(_ context.Context, _ *api.SubmitAttestationsOpts) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ func NewAggregateAttestationsSubmitter() eth2client.AggregateAttestationsSubmitt
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SubmitAggregateAttestations is a mock.
|
// SubmitAggregateAttestations is a mock.
|
||||||
func (m *AggregateAttestationsSubmitter) SubmitAggregateAttestations(_ context.Context, _ []*phase0.SignedAggregateAndProof) error {
|
func (m *AggregateAttestationsSubmitter) SubmitAggregateAttestations(_ context.Context, _ *api.SubmitAggregateAttestationsOpts) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,19 +17,25 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/attestantio/go-eth2-client/spec"
|
||||||
"github.com/attestantio/go-eth2-client/spec/phase0"
|
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/wealdtech/ethdo/services/chaintime"
|
"github.com/wealdtech/ethdo/services/chaintime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AttestationHead returns the head for which the attestation should have voted.
|
// AttestationHead returns the head for which the attestation should have voted.
|
||||||
func AttestationHead(ctx context.Context,
|
func AttestationHead(ctx context.Context,
|
||||||
headersCache *BeaconBlockHeaderCache,
|
headersCache *BeaconBlockHeaderCache,
|
||||||
attestation *phase0.Attestation,
|
attestation *spec.VersionedAttestation,
|
||||||
) (
|
) (
|
||||||
phase0.Root,
|
phase0.Root,
|
||||||
error,
|
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 {
|
for {
|
||||||
header, err := headersCache.Fetch(ctx, slot)
|
header, err := headersCache.Fetch(ctx, slot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -53,12 +59,16 @@ func AttestationHead(ctx context.Context,
|
|||||||
// AttestationHeadCorrect returns true if the given attestation had the correct head.
|
// AttestationHeadCorrect returns true if the given attestation had the correct head.
|
||||||
func AttestationHeadCorrect(ctx context.Context,
|
func AttestationHeadCorrect(ctx context.Context,
|
||||||
headersCache *BeaconBlockHeaderCache,
|
headersCache *BeaconBlockHeaderCache,
|
||||||
attestation *phase0.Attestation,
|
attestation *spec.VersionedAttestation,
|
||||||
) (
|
) (
|
||||||
bool,
|
bool,
|
||||||
error,
|
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 {
|
for {
|
||||||
header, err := headersCache.Fetch(ctx, slot)
|
header, err := headersCache.Fetch(ctx, slot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -74,7 +84,7 @@ func AttestationHeadCorrect(ctx context.Context,
|
|||||||
slot--
|
slot--
|
||||||
continue
|
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,
|
func AttestationTarget(ctx context.Context,
|
||||||
headersCache *BeaconBlockHeaderCache,
|
headersCache *BeaconBlockHeaderCache,
|
||||||
chainTime chaintime.Service,
|
chainTime chaintime.Service,
|
||||||
attestation *phase0.Attestation,
|
attestation *spec.VersionedAttestation,
|
||||||
) (
|
) (
|
||||||
phase0.Root,
|
phase0.Root,
|
||||||
error,
|
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.
|
// Start with first slot of the target epoch.
|
||||||
slot := chainTime.FirstSlotOfEpoch(attestation.Data.Target.Epoch)
|
slot := chainTime.FirstSlotOfEpoch(attestationData.Target.Epoch)
|
||||||
for {
|
for {
|
||||||
header, err := headersCache.Fetch(ctx, slot)
|
header, err := headersCache.Fetch(ctx, slot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -113,13 +127,17 @@ func AttestationTarget(ctx context.Context,
|
|||||||
func AttestationTargetCorrect(ctx context.Context,
|
func AttestationTargetCorrect(ctx context.Context,
|
||||||
headersCache *BeaconBlockHeaderCache,
|
headersCache *BeaconBlockHeaderCache,
|
||||||
chainTime chaintime.Service,
|
chainTime chaintime.Service,
|
||||||
attestation *phase0.Attestation,
|
attestation *spec.VersionedAttestation,
|
||||||
) (
|
) (
|
||||||
bool,
|
bool,
|
||||||
error,
|
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.
|
// Start with first slot of the target epoch.
|
||||||
slot := chainTime.FirstSlotOfEpoch(attestation.Data.Target.Epoch)
|
slot := chainTime.FirstSlotOfEpoch(attestationData.Target.Epoch)
|
||||||
for {
|
for {
|
||||||
header, err := headersCache.Fetch(ctx, slot)
|
header, err := headersCache.Fetch(ctx, slot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -135,6 +153,6 @@ func AttestationTargetCorrect(ctx context.Context,
|
|||||||
slot--
|
slot--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return bytes.Equal(header.Root[:], attestation.Data.Target.Root[:]), nil
|
return bytes.Equal(header.Root[:], attestationData.Target.Root[:]), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user