Compare commits

...

3 Commits

Author SHA1 Message Date
Jim McDonald
bb34776d05 Return on error. 2024-05-27 09:34:06 +01:00
Jim McDonald
e29818340d Additional electra support. 2024-05-15 00:06:52 +01:00
Jim McDonald
083f484907 Support for custom spec. 2024-05-14 23:14:46 +01:00
55 changed files with 991 additions and 300 deletions

View File

@@ -4,6 +4,16 @@
# This file is not a configuration example,
# it contains the exhaustive configuration with explanations of the options.
issues:
# Which files to exclude: they will be analyzed, but issues from them won't be reported.
# There is no need to include all autogenerated files,
# we confidently recognize autogenerated files.
# If it's not, please let us know.
# "/" will be replaced by current OS file path separator to properly work on Windows.
# Default: []
exclude-files:
- ".*_ssz\\.go$"
# Options for analysis running.
run:
# The default concurrency value is the number of available CPU.
@@ -39,15 +49,6 @@ run:
# Default: true
# skip-dirs-use-default: false
# Which files to skip: they will be analyzed, but issues from them won't be reported.
# Default value is empty list,
# but there is no need to include all autogenerated files,
# we confidently recognize autogenerated files.
# If it's not please let us know.
# "/" will be replaced by current OS file path separator to properly work on Windows.
skip-files:
- ".*_ssz\\.go$"
# If set we pass it to "go list -mod={option}". From "go help modules":
# If invoked with -mod=readonly, the go command is disallowed from the implicit
# automatic updating of go.mod described above. Instead, it fails when any changes
@@ -68,7 +69,7 @@ run:
# Define the Go version limit.
# Mainly related to generics support since go1.18.
# Default: use Go version from the go.mod file, fallback on the env var `GOVERSION`, fallback on 1.18
go: '1.19'
# go: '1.19'
# output configuration options
@@ -130,7 +131,9 @@ linters:
- deadcode
- depguard
- dupl
- err113
- errorlint
- execinquery
- exhaustive
- exhaustivestruct
- exhaustruct
@@ -142,7 +145,6 @@ linters:
- gochecknoinits
- gocognit
- goconst
- goerr113
- goheader
- golint
- gomnd
@@ -152,6 +154,7 @@ linters:
- lll
- maintidx
- maligned
- mnd
- musttag
- nestif
- nilnil

View File

@@ -24,7 +24,7 @@ import (
var accountImportCmd = &cobra.Command{
Use: "import",
Short: "Import an account",
Long: `Import an account from its private key. For example:
Long: `Import an account from its private key or keystore. For example:
ethdo account import --account="primary/testing" --key="0x..." --passphrase="my secret"

View File

@@ -61,10 +61,11 @@ func input(ctx context.Context) (*dataIn, error) {
// Ethereum 2 client.
var err error
data.eth2Client, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
LogFallback: !data.quiet,
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
CustomSpecSupport: viper.GetBool("custom-spec"),
LogFallback: !data.quiet,
})
if err != nil {
return nil, err

View File

@@ -58,10 +58,11 @@ func input(ctx context.Context) (*dataIn, error) {
// Ethereum 2 client.
var err error
data.eth2Client, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
LogFallback: !data.quiet,
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
CustomSpecSupport: viper.GetBool("custom-spec"),
LogFallback: !data.quiet,
})
if err != nil {
return nil, err

View File

@@ -93,23 +93,33 @@ func process(ctx context.Context, data *dataIn) (*dataOut, error) {
return nil, errors.Wrap(err, "failed to obtain block attestations")
}
for i, attestation := range attestations {
if attestation.Data.Slot == duty.Slot &&
attestation.Data.Index == duty.CommitteeIndex &&
attestation.AggregationBits.BitAt(duty.ValidatorCommitteeIndex) {
attestationData, err := attestation.Data()
if err != nil {
return nil, errors.Wrap(err, "failed to obtain attestation data")
}
aggregationBits, err := attestation.AggregationBits()
if err != nil {
return nil, errors.Wrap(err, "failed to obtain attestation aggregation bits")
}
if attestationData.Slot == duty.Slot &&
attestationData.Index == duty.CommitteeIndex &&
aggregationBits.BitAt(duty.ValidatorCommitteeIndex) {
headCorrect := false
targetCorrect := false
if data.verbose {
headCorrect, err = calcHeadCorrect(ctx, data, attestation)
headCorrect, err = calcHeadCorrect(ctx, data, attestationData)
if err != nil {
return nil, errors.Wrap(err, "failed to obtain head correct result")
}
targetCorrect, err = calcTargetCorrect(ctx, data, attestation)
targetCorrect, err = calcTargetCorrect(ctx, data, attestationData)
if err != nil {
return nil, errors.Wrap(err, "failed to obtain target correct result")
}
}
results.found = true
results.attestation = attestation
// TODO fix.
// results.attestation = attestation
results.slot = slot
results.attestationIndex = uint64(i)
results.inclusionDelay = slot - duty.Slot
@@ -128,8 +138,8 @@ func process(ctx context.Context, data *dataIn) (*dataOut, error) {
return results, nil
}
func calcHeadCorrect(ctx context.Context, data *dataIn, attestation *phase0.Attestation) (bool, error) {
slot := attestation.Data.Slot
func calcHeadCorrect(ctx context.Context, data *dataIn, attestationData *phase0.AttestationData) (bool, error) {
slot := attestationData.Slot
for {
response, err := data.eth2Client.(eth2client.BeaconBlockHeadersProvider).BeaconBlockHeader(ctx, &api.BeaconBlockHeaderOpts{
Block: fmt.Sprintf("%d", slot),
@@ -149,13 +159,13 @@ func calcHeadCorrect(ctx context.Context, data *dataIn, attestation *phase0.Atte
slot--
continue
}
return bytes.Equal(response.Data.Root[:], attestation.Data.BeaconBlockRoot[:]), nil
return bytes.Equal(response.Data.Root[:], attestationData.BeaconBlockRoot[:]), nil
}
}
func calcTargetCorrect(ctx context.Context, data *dataIn, attestation *phase0.Attestation) (bool, error) {
func calcTargetCorrect(ctx context.Context, data *dataIn, attestationData *phase0.AttestationData) (bool, error) {
// Start with first slot of the target epoch.
slot := data.chainTime.FirstSlotOfEpoch(attestation.Data.Target.Epoch)
slot := data.chainTime.FirstSlotOfEpoch(attestationData.Target.Epoch)
for {
response, err := data.eth2Client.(eth2client.BeaconBlockHeadersProvider).BeaconBlockHeader(ctx, &api.BeaconBlockHeaderOpts{
Block: fmt.Sprintf("%d", slot),
@@ -175,7 +185,7 @@ func calcTargetCorrect(ctx context.Context, data *dataIn, attestation *phase0.At
slot--
continue
}
return bytes.Equal(response.Data.Root[:], attestation.Data.Target.Root[:]), nil
return bytes.Equal(response.Data.Root[:], attestationData.Target.Root[:]), nil
}
}

View File

@@ -34,6 +34,7 @@ type command struct {
timeout time.Duration
connection string
allowInsecureConnections bool
customSpecSupport bool
// Operation.
blockID string
@@ -124,6 +125,7 @@ func newCommand(_ context.Context) (*command, error) {
c.connection = viper.GetString("connection")
c.allowInsecureConnections = viper.GetBool("allow-insecure-connections")
c.customSpecSupport = viper.GetBool("custom-spec")
c.blockID = viper.GetString("blockid")
c.stream = viper.GetBool("stream")

View File

@@ -21,6 +21,7 @@ import (
eth2client "github.com/attestantio/go-eth2-client"
"github.com/attestantio/go-eth2-client/api"
"github.com/attestantio/go-eth2-client/spec"
"github.com/attestantio/go-eth2-client/spec/electra"
"github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-bitfield"
@@ -62,8 +63,12 @@ func (c *command) process(ctx context.Context) error {
// Calculate how many parents we need to fetch.
minSlot := slot
for _, attestation := range attestations {
if attestation.Data.Slot < minSlot {
minSlot = attestation.Data.Slot
attestationData, err := attestation.Data()
if err != nil {
continue
}
if attestationData.Slot < minSlot {
minSlot = attestationData.Slot
}
}
if c.debug {
@@ -102,72 +107,27 @@ func (c *command) analyzeAttestations(ctx context.Context, block *spec.Versioned
if c.debug {
fmt.Printf("Processing attestation %d\n", i)
}
analysis := &attestationAnalysis{
Head: attestation.Data.BeaconBlockRoot,
Target: attestation.Data.Target.Root,
Distance: int(slot - attestation.Data.Slot),
}
root, err := attestation.HashTreeRoot()
var analysis *attestationAnalysis
switch attestation.Version {
case spec.DataVersionPhase0:
analysis, err = c.phase0Analysis(ctx, slot, attestation.Phase0, blockVotes, spec.DataVersionPhase0)
case spec.DataVersionAltair:
analysis, err = c.phase0Analysis(ctx, slot, attestation.Altair, blockVotes, spec.DataVersionAltair)
case spec.DataVersionBellatrix:
analysis, err = c.phase0Analysis(ctx, slot, attestation.Bellatrix, blockVotes, spec.DataVersionBellatrix)
case spec.DataVersionCapella:
analysis, err = c.phase0Analysis(ctx, slot, attestation.Capella, blockVotes, spec.DataVersionCapella)
case spec.DataVersionDeneb:
analysis, err = c.phase0Analysis(ctx, slot, attestation.Deneb, blockVotes, spec.DataVersionDeneb)
case spec.DataVersionElectra:
analysis, err = c.electraAnalysis(ctx, slot, attestation.Electra, blockVotes, spec.DataVersionElectra)
default:
return fmt.Errorf("unknown version %s", attestation.Version)
}
if err != nil {
return err
}
if info, exists := c.priorAttestations[fmt.Sprintf("%#x", root)]; exists {
analysis.Duplicate = info
} else {
data := attestation.Data
_, exists := blockVotes[data.Slot]
if !exists {
blockVotes[data.Slot] = make(map[phase0.CommitteeIndex]bitfield.Bitlist)
}
_, exists = blockVotes[data.Slot][data.Index]
if !exists {
blockVotes[data.Slot][data.Index] = bitfield.NewBitlist(attestation.AggregationBits.Len())
}
// Count new votes.
analysis.PossibleVotes = int(attestation.AggregationBits.Len())
for j := uint64(0); j < attestation.AggregationBits.Len(); j++ {
if attestation.AggregationBits.BitAt(j) {
analysis.Votes++
if blockVotes[data.Slot][data.Index].BitAt(j) {
// Already attested to in this block; skip.
continue
}
if c.votes[data.Slot][data.Index].BitAt(j) {
// Already attested to in a previous block; skip.
continue
}
analysis.NewVotes++
blockVotes[data.Slot][data.Index].SetBitAt(j, true)
}
}
// Calculate head correct.
var err error
analysis.HeadCorrect, err = c.calcHeadCorrect(ctx, attestation)
if err != nil {
return err
}
// Calculate head timely.
analysis.HeadTimely = analysis.HeadCorrect && attestation.Data.Slot == slot-1
// Calculate source timely.
analysis.SourceTimely = attestation.Data.Slot >= slot-5
// Calculate target correct.
analysis.TargetCorrect, err = c.calcTargetCorrect(ctx, attestation)
if err != nil {
return err
}
// Calculate target timely.
if block.Version < spec.DataVersionDeneb {
analysis.TargetTimely = attestation.Data.Slot >= slot-32
} else {
analysis.TargetTimely = true
}
}
// Calculate score and value.
if analysis.TargetCorrect && analysis.TargetTimely {
@@ -188,6 +148,165 @@ func (c *command) analyzeAttestations(ctx context.Context, block *spec.Versioned
return nil
}
func (c *command) phase0Analysis(ctx context.Context,
slot phase0.Slot,
attestation *phase0.Attestation,
blockVotes map[phase0.Slot]map[phase0.CommitteeIndex]bitfield.Bitlist,
version spec.DataVersion,
) (
*attestationAnalysis,
error,
) {
analysis := &attestationAnalysis{
Head: attestation.Data.BeaconBlockRoot,
Target: attestation.Data.Target.Root,
Distance: int(slot - attestation.Data.Slot),
}
// TODO.
root, err := attestation.HashTreeRoot()
if err != nil {
return nil, err
}
if info, exists := c.priorAttestations[fmt.Sprintf("%#x", root)]; exists {
analysis.Duplicate = info
} else {
data := attestation.Data
_, exists := blockVotes[data.Slot]
if !exists {
blockVotes[data.Slot] = make(map[phase0.CommitteeIndex]bitfield.Bitlist)
}
_, exists = blockVotes[data.Slot][data.Index]
if !exists {
blockVotes[data.Slot][data.Index] = bitfield.NewBitlist(attestation.AggregationBits.Len())
}
// Count new votes.
analysis.PossibleVotes = int(attestation.AggregationBits.Len())
for j := uint64(0); j < attestation.AggregationBits.Len(); j++ {
if attestation.AggregationBits.BitAt(j) {
analysis.Votes++
if blockVotes[data.Slot][data.Index].BitAt(j) {
// Already attested to in this block; skip.
continue
}
if c.votes[data.Slot][data.Index].BitAt(j) {
// Already attested to in a previous block; skip.
continue
}
analysis.NewVotes++
blockVotes[data.Slot][data.Index].SetBitAt(j, true)
}
}
// Calculate head correct.
analysis.HeadCorrect, err = c.calcHeadCorrect(ctx, attestation)
if err != nil {
return nil, err
}
// Calculate head timely.
analysis.HeadTimely = analysis.HeadCorrect && attestation.Data.Slot == slot-1
// Calculate source timely.
analysis.SourceTimely = attestation.Data.Slot >= slot-5
// Calculate target correct.
analysis.TargetCorrect, err = c.calcTargetCorrect(ctx, attestation)
if err != nil {
return nil, err
}
// Calculate target timely.
if version < spec.DataVersionDeneb {
analysis.TargetTimely = attestation.Data.Slot >= slot-32
} else {
analysis.TargetTimely = true
}
}
return analysis, nil
}
func (c *command) electraAnalysis(ctx context.Context,
slot phase0.Slot,
attestation *electra.Attestation,
blockVotes map[phase0.Slot]map[phase0.CommitteeIndex]bitfield.Bitlist,
version spec.DataVersion,
) (
*attestationAnalysis,
error,
) {
analysis := &attestationAnalysis{
Head: attestation.Data.BeaconBlockRoot,
Target: attestation.Data.Target.Root,
Distance: int(slot - attestation.Data.Slot),
}
// TODO.
root, err := attestation.HashTreeRoot()
if err != nil {
return nil, err
}
if info, exists := c.priorAttestations[fmt.Sprintf("%#x", root)]; exists {
analysis.Duplicate = info
} else {
data := attestation.Data
_, exists := blockVotes[data.Slot]
if !exists {
blockVotes[data.Slot] = make(map[phase0.CommitteeIndex]bitfield.Bitlist)
}
_, exists = blockVotes[data.Slot][data.Index]
if !exists {
blockVotes[data.Slot][data.Index] = bitfield.NewBitlist(attestation.AggregationBits.Len())
}
// Count new votes.
analysis.PossibleVotes = int(attestation.AggregationBits.Len())
for j := uint64(0); j < attestation.AggregationBits.Len(); j++ {
if attestation.AggregationBits.BitAt(j) {
analysis.Votes++
if blockVotes[data.Slot][data.Index].BitAt(j) {
// Already attested to in this block; skip.
continue
}
if c.votes[data.Slot][data.Index].BitAt(j) {
// Already attested to in a previous block; skip.
continue
}
analysis.NewVotes++
blockVotes[data.Slot][data.Index].SetBitAt(j, true)
}
}
// Calculate head correct.
var err error
analysis.HeadCorrect, err = c.calcElectraHeadCorrect(ctx, attestation)
if err != nil {
return nil, err
}
// Calculate head timely.
analysis.HeadTimely = analysis.HeadCorrect && attestation.Data.Slot == slot-1
// Calculate source timely.
analysis.SourceTimely = attestation.Data.Slot >= slot-5
// Calculate target correct.
analysis.TargetCorrect, err = c.calcElectraTargetCorrect(ctx, attestation)
if err != nil {
return nil, err
}
// Calculate target timely.
if version < spec.DataVersionDeneb {
analysis.TargetTimely = attestation.Data.Slot >= slot-32
} else {
analysis.TargetTimely = true
}
}
return analysis, nil
}
func (c *command) fetchParents(ctx context.Context, block *spec.VersionedSignedBeaconBlock, minSlot phase0.Slot) error {
parentRoot, err := block.ParentRoot()
if err != nil {
@@ -237,10 +356,10 @@ func (c *command) fetchParents(ctx context.Context, block *spec.VersionedSignedB
}
func (c *command) processParentBlock(_ context.Context, block *spec.VersionedSignedBeaconBlock) error {
attestations, err := block.Attestations()
if err != nil {
return err
}
// attestations, err := block.Attestations()
// if err != nil {
// return err
// }
slot, err := block.Slot()
if err != nil {
return err
@@ -249,31 +368,40 @@ func (c *command) processParentBlock(_ context.Context, block *spec.VersionedSig
fmt.Printf("Processing block %d\n", slot)
}
for i, attestation := range attestations {
root, err := attestation.HashTreeRoot()
if err != nil {
return err
}
c.priorAttestations[fmt.Sprintf("%#x", root)] = &attestationData{
Block: slot,
Index: i,
}
// TODO reinstate.
// for i, attestation := range attestations {
// root, err := attestation.HashTreeRoot()
// if err != nil {
// return err
// }
// c.priorAttestations[fmt.Sprintf("%#x", root)] = &attestationData{
// Block: slot,
// Index: i,
// }
data := attestation.Data
_, exists := c.votes[data.Slot]
if !exists {
c.votes[data.Slot] = make(map[phase0.CommitteeIndex]bitfield.Bitlist)
}
_, exists = c.votes[data.Slot][data.Index]
if !exists {
c.votes[data.Slot][data.Index] = bitfield.NewBitlist(attestation.AggregationBits.Len())
}
for j := uint64(0); j < attestation.AggregationBits.Len(); j++ {
if attestation.AggregationBits.BitAt(j) {
c.votes[data.Slot][data.Index].SetBitAt(j, true)
}
}
}
// data, err := attestation.Data()
// if err != nil {
// return err
// }
// aggregationBits, err := attestation.AggregationBits()
// if err != nil {
// return err
// }
// _, exists := c.votes[data.Slot]
// if !exists {
// c.votes[data.Slot] = make(map[phase0.CommitteeIndex]bitfield.Bitlist)
// }
// _, exists = c.votes[data.Slot][data.Index]
// if !exists {
// c.votes[data.Slot][data.Index] = bitfield.NewBitlist(aggregationBits.Len())
// }
// for j := uint64(0); j < aggregationBits.Len(); j++ {
// if aggregationBits.BitAt(j) {
// c.votes[data.Slot][data.Index].SetBitAt(j, true)
// }
// }
//}
return nil
}
@@ -283,10 +411,11 @@ func (c *command) setup(ctx context.Context) error {
// Connect to the client.
c.eth2Client, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
LogFallback: !c.quiet,
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
CustomSpecSupport: c.customSpecSupport,
LogFallback: !c.quiet,
})
if err != nil {
return errors.Wrap(err, "failed to connect to beacon node")
@@ -457,6 +586,79 @@ func (c *command) calcTargetCorrect(ctx context.Context, attestation *phase0.Att
return bytes.Equal(root[:], attestation.Data.Target.Root[:]), nil
}
func (c *command) calcElectraHeadCorrect(ctx context.Context, attestation *electra.Attestation) (bool, error) {
slot := attestation.Data.Slot
root, exists := c.headRoots[slot]
if !exists {
for {
response, err := c.blockHeadersProvider.BeaconBlockHeader(ctx, &api.BeaconBlockHeaderOpts{
Block: fmt.Sprintf("%d", slot),
})
if err != nil {
var apiError *api.Error
if errors.As(err, &apiError) && apiError.StatusCode == 404 {
if c.debug {
fmt.Printf("No block available for slot %d, assuming not in canonical chain", slot)
}
return false, nil
}
return false, err
}
if response.Data == nil {
// No block.
slot--
continue
}
if !response.Data.Canonical {
// Not canonical.
slot--
continue
}
c.headRoots[attestation.Data.Slot] = response.Data.Root
root = response.Data.Root
break
}
}
return bytes.Equal(root[:], attestation.Data.BeaconBlockRoot[:]), nil
}
func (c *command) calcElectraTargetCorrect(ctx context.Context, attestation *electra.Attestation) (bool, error) {
root, exists := c.targetRoots[attestation.Data.Slot]
if !exists {
// Start with first slot of the target epoch.
slot := c.chainTime.FirstSlotOfEpoch(attestation.Data.Target.Epoch)
for {
response, err := c.blockHeadersProvider.BeaconBlockHeader(ctx, &api.BeaconBlockHeaderOpts{
Block: fmt.Sprintf("%d", slot),
})
if err != nil {
var apiError *api.Error
if errors.As(err, &apiError) && apiError.StatusCode == 404 {
if c.debug {
fmt.Printf("No block available for slot %d, assuming not in canonical chain", slot)
}
return false, nil
}
}
if response.Data == nil {
// No block.
slot--
continue
}
if !response.Data.Canonical {
// Not canonical.
slot--
continue
}
c.targetRoots[attestation.Data.Slot] = response.Data.Root
root = response.Data.Root
break
}
}
return bytes.Equal(root[:], attestation.Data.Target.Root[:]), nil
}
func (c *command) analyzeSyncCommittees(_ context.Context, block *spec.VersionedSignedBeaconBlock) error {
c.analysis.SyncCommitee = &syncCommitteeAnalysis{}
switch block.Version {

View File

@@ -57,10 +57,11 @@ func input(ctx context.Context) (*dataIn, error) {
var err error
data.eth2Client, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
LogFallback: !data.quiet,
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
CustomSpecSupport: viper.GetBool("custom-spec"),
LogFallback: !data.quiet,
})
if err != nil {
return nil, err

View File

@@ -31,6 +31,7 @@ import (
"github.com/attestantio/go-eth2-client/spec/bellatrix"
"github.com/attestantio/go-eth2-client/spec/capella"
"github.com/attestantio/go-eth2-client/spec/deneb"
"github.com/attestantio/go-eth2-client/spec/electra"
"github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-bitfield"
@@ -204,6 +205,120 @@ func outputBlockAttesterSlashings(ctx context.Context, eth2Client eth2client.Ser
return res.String(), nil
}
func outputElectraBlockAttestations(ctx context.Context,
eth2Client eth2client.Service,
verbose bool,
attestations []*electra.Attestation,
) (
string,
error,
) {
res := strings.Builder{}
validatorCommittees := make(map[phase0.Slot]map[phase0.CommitteeIndex][]phase0.ValidatorIndex)
res.WriteString(fmt.Sprintf("Attestations: %d\n", len(attestations)))
if verbose {
beaconCommitteesProvider, isProvider := eth2Client.(eth2client.BeaconCommitteesProvider)
if isProvider {
for i, att := range attestations {
res.WriteString(fmt.Sprintf(" %d:\n", i))
// Fetch committees for this epoch if not already obtained.
committees, exists := validatorCommittees[att.Data.Slot]
if !exists {
response, err := beaconCommitteesProvider.BeaconCommittees(ctx, &api.BeaconCommitteesOpts{
State: fmt.Sprintf("%d", att.Data.Slot),
})
if err != nil {
// Failed to get it; create an empty committee to stop us continually attempting to re-fetch.
validatorCommittees[att.Data.Slot] = make(map[phase0.CommitteeIndex][]phase0.ValidatorIndex)
} else {
for _, beaconCommittee := range response.Data {
if _, exists := validatorCommittees[beaconCommittee.Slot]; !exists {
validatorCommittees[beaconCommittee.Slot] = make(map[phase0.CommitteeIndex][]phase0.ValidatorIndex)
}
validatorCommittees[beaconCommittee.Slot][beaconCommittee.Index] = beaconCommittee.Validators
}
}
committees = validatorCommittees[att.Data.Slot]
}
res.WriteString(fmt.Sprintf(" Committee index: %d\n", att.Data.Index))
res.WriteString(fmt.Sprintf(" Attesters: %d/%d\n", att.AggregationBits.Count(), att.AggregationBits.Len()))
res.WriteString(fmt.Sprintf(" Aggregation bits: %s\n", bitlistToString(att.AggregationBits)))
if _, exists := committees[att.Data.Index]; exists {
res.WriteString(fmt.Sprintf(" Attesting indices: %s\n", attestingIndices(att.AggregationBits, committees[att.Data.Index])))
}
res.WriteString(fmt.Sprintf(" Slot: %d\n", att.Data.Slot))
res.WriteString(fmt.Sprintf(" Beacon block root: %#x\n", att.Data.BeaconBlockRoot))
res.WriteString(fmt.Sprintf(" Source epoch: %d\n", att.Data.Source.Epoch))
res.WriteString(fmt.Sprintf(" Source root: %#x\n", att.Data.Source.Root))
res.WriteString(fmt.Sprintf(" Target epoch: %d\n", att.Data.Target.Epoch))
res.WriteString(fmt.Sprintf(" Target root: %#x\n", att.Data.Target.Root))
}
}
}
return res.String(), nil
}
func outputElectraBlockAttesterSlashings(ctx context.Context,
eth2Client eth2client.Service,
verbose bool,
attesterSlashings []*electra.AttesterSlashing,
) (
string,
error,
) {
res := strings.Builder{}
res.WriteString(fmt.Sprintf("Attester slashings: %d\n", len(attesterSlashings)))
if verbose {
for i, slashing := range attesterSlashings {
// Say what was slashed.
att1 := slashing.Attestation1
att2 := slashing.Attestation2
slashedIndices := intersection(att1.AttestingIndices, att2.AttestingIndices)
if len(slashedIndices) == 0 {
continue
}
res.WriteString(fmt.Sprintf(" %d:\n", i))
res.WriteString(fmt.Sprintln(" Slashed validators:"))
response, err := eth2Client.(eth2client.ValidatorsProvider).Validators(ctx, &api.ValidatorsOpts{
State: "head",
Indices: slashedIndices,
})
if err != nil {
return "", errors.Wrap(err, "failed to obtain beacon committees")
}
for k, v := range response.Data {
res.WriteString(fmt.Sprintf(" %#x (%d)\n", v.Validator.PublicKey[:], k))
}
// Say what caused the slashing.
if att1.Data.Target.Epoch == att2.Data.Target.Epoch {
res.WriteString(fmt.Sprintf(" Double voted for same target epoch (%d):\n", att1.Data.Target.Epoch))
if !bytes.Equal(att1.Data.Target.Root[:], att2.Data.Target.Root[:]) {
res.WriteString(fmt.Sprintf(" Attestation 1 target epoch root: %#x\n", att1.Data.Target.Root))
res.WriteString(fmt.Sprintf(" Attestation 2target epoch root: %#x\n", att2.Data.Target.Root))
}
if !bytes.Equal(att1.Data.BeaconBlockRoot[:], att2.Data.BeaconBlockRoot[:]) {
res.WriteString(fmt.Sprintf(" Attestation 1 beacon block root: %#x\n", att1.Data.BeaconBlockRoot))
res.WriteString(fmt.Sprintf(" Attestation 2 beacon block root: %#x\n", att2.Data.BeaconBlockRoot))
}
} else if att1.Data.Source.Epoch < att2.Data.Source.Epoch &&
att1.Data.Target.Epoch > att2.Data.Target.Epoch {
res.WriteString(" Surround voted:\n")
res.WriteString(fmt.Sprintf(" Attestation 1 vote: %d->%d\n", att1.Data.Source.Epoch, att1.Data.Target.Epoch))
res.WriteString(fmt.Sprintf(" Attestation 2 vote: %d->%d\n", att2.Data.Source.Epoch, att2.Data.Target.Epoch))
}
}
}
return res.String(), nil
}
func outputBlockDeposits(_ context.Context, verbose bool, deposits []*phase0.Deposit) (string, error) {
res := strings.Builder{}
@@ -517,6 +632,116 @@ func outputDenebBlockText(ctx context.Context,
return res.String(), nil
}
func outputElectraBlockText(ctx context.Context,
data *dataOut,
signedBlock *electra.SignedBeaconBlock,
blobs []*deneb.BlobSidecar,
) (
string,
error,
) {
if signedBlock == nil {
return "", errors.New("no block supplied")
}
body := signedBlock.Message.Body
res := strings.Builder{}
// General info.
blockRoot, err := signedBlock.Message.HashTreeRoot()
if err != nil {
return "", errors.Wrap(err, "failed to obtain block root")
}
bodyRoot, err := signedBlock.Message.Body.HashTreeRoot()
if err != nil {
return "", errors.Wrap(err, "failed to generate body root")
}
tmp, err := outputBlockGeneral(ctx,
data.verbose,
signedBlock.Message.Slot,
signedBlock.Message.ProposerIndex,
blockRoot,
bodyRoot,
signedBlock.Message.ParentRoot,
signedBlock.Message.StateRoot,
signedBlock.Message.Body.Graffiti[:],
data.genesisTime,
data.slotDuration,
data.slotsPerEpoch)
if err != nil {
return "", err
}
res.WriteString(tmp)
// Eth1 data.
if data.verbose {
tmp, err := outputBlockETH1Data(ctx, body.ETH1Data)
if err != nil {
return "", err
}
res.WriteString(tmp)
}
// Sync aggregate.
tmp, err = outputBlockSyncAggregate(ctx, data.eth2Client, data.verbose, signedBlock.Message.Body.SyncAggregate, phase0.Epoch(uint64(signedBlock.Message.Slot)/data.slotsPerEpoch))
if err != nil {
return "", err
}
res.WriteString(tmp)
// Attestations.
tmp, err = outputElectraBlockAttestations(ctx, data.eth2Client, data.verbose, signedBlock.Message.Body.Attestations)
if err != nil {
return "", err
}
res.WriteString(tmp)
// Attester slashings.
tmp, err = outputElectraBlockAttesterSlashings(ctx, data.eth2Client, data.verbose, signedBlock.Message.Body.AttesterSlashings)
if err != nil {
return "", err
}
res.WriteString(tmp)
res.WriteString(fmt.Sprintf("Proposer slashings: %d\n", len(body.ProposerSlashings)))
// Add verbose proposer slashings.
tmp, err = outputBlockDeposits(ctx, data.verbose, signedBlock.Message.Body.Deposits)
if err != nil {
return "", err
}
res.WriteString(tmp)
// Voluntary exits.
tmp, err = outputBlockVoluntaryExits(ctx, data.eth2Client, data.verbose, signedBlock.Message.Body.VoluntaryExits)
if err != nil {
return "", err
}
res.WriteString(tmp)
tmp, err = outputBlockBLSToExecutionChanges(ctx, data.eth2Client, data.verbose, signedBlock.Message.Body.BLSToExecutionChanges)
if err != nil {
return "", err
}
res.WriteString(tmp)
tmp, err = outputElectraBlockExecutionPayload(ctx, data.verbose, signedBlock.Message.Body.ExecutionPayload)
if err != nil {
return "", err
}
res.WriteString(tmp)
tmp, err = outputElectraBlobInfo(ctx, data.verbose, signedBlock.Message.Body, blobs)
if err != nil {
return "", err
}
res.WriteString(tmp)
return res.String(), nil
}
func outputBellatrixBlockText(ctx context.Context, data *dataOut, signedBlock *bellatrix.SignedBeaconBlock) (string, error) {
if signedBlock == nil {
return "", errors.New("no block supplied")
@@ -903,6 +1128,71 @@ func outputDenebBlockExecutionPayload(_ context.Context,
return res.String(), nil
}
func outputElectraBlockExecutionPayload(_ context.Context,
verbose bool,
payload *electra.ExecutionPayload,
) (
string,
error,
) {
if payload == nil {
return "", nil
}
// If the block number is 0 then we're before the merge.
if payload.BlockNumber == 0 {
return "", nil
}
res := strings.Builder{}
if !verbose {
res.WriteString("Execution block number: ")
res.WriteString(fmt.Sprintf("%d\n", payload.BlockNumber))
res.WriteString("Transactions: ")
res.WriteString(fmt.Sprintf("%d\n", len(payload.Transactions)))
} else {
res.WriteString("Execution payload:\n")
res.WriteString(" Execution block number: ")
res.WriteString(fmt.Sprintf("%d\n", payload.BlockNumber))
res.WriteString(" Base fee per gas: ")
res.WriteString(string2eth.WeiToString(payload.BaseFeePerGas.ToBig(), true))
res.WriteString("\n Block hash: ")
res.WriteString(fmt.Sprintf("%#x\n", payload.BlockHash))
res.WriteString(" Parent hash: ")
res.WriteString(fmt.Sprintf("%#x\n", payload.ParentHash))
res.WriteString(" Fee recipient: ")
res.WriteString(payload.FeeRecipient.String())
res.WriteString(" Gas limit: ")
res.WriteString(fmt.Sprintf("%d\n", payload.GasLimit))
res.WriteString(" Gas used: ")
res.WriteString(fmt.Sprintf("%d\n", payload.GasUsed))
res.WriteString(" Timestamp: ")
res.WriteString(fmt.Sprintf("%s (%d)\n", time.Unix(int64(payload.Timestamp), 0).String(), payload.Timestamp))
res.WriteString(" Prev RANDAO: ")
res.WriteString(fmt.Sprintf("%#x\n", payload.PrevRandao))
res.WriteString(" Receipts root: ")
res.WriteString(fmt.Sprintf("%#x\n", payload.ReceiptsRoot))
res.WriteString(" State root: ")
res.WriteString(fmt.Sprintf("%#x\n", payload.StateRoot))
res.WriteString(" Extra data: ")
if utf8.Valid(payload.ExtraData) {
res.WriteString(fmt.Sprintf("%s\n", string(payload.ExtraData)))
} else {
res.WriteString(fmt.Sprintf("%#x\n", payload.ExtraData))
}
res.WriteString(" Logs bloom: ")
res.WriteString(fmt.Sprintf("%#x\n", payload.LogsBloom))
res.WriteString(" Transactions: ")
res.WriteString(fmt.Sprintf("%d\n", len(payload.Transactions)))
res.WriteString(" Withdrawals: ")
res.WriteString(fmt.Sprintf("%d\n", len(payload.Withdrawals)))
res.WriteString(" Excess blob gas: ")
res.WriteString(fmt.Sprintf("%d\n", payload.ExcessBlobGas))
}
return res.String(), nil
}
func outputDenebBlobInfo(_ context.Context,
verbose bool,
body *deneb.BeaconBlockBody,
@@ -932,6 +1222,35 @@ func outputDenebBlobInfo(_ context.Context,
return res.String(), nil
}
func outputElectraBlobInfo(_ context.Context,
verbose bool,
body *electra.BeaconBlockBody,
blobs []*deneb.BlobSidecar,
) (
string,
error,
) {
if body == nil {
return "", nil
}
if !verbose {
return fmt.Sprintf("Blobs: %d\n", len(body.BlobKZGCommitments)), nil
}
res := strings.Builder{}
for i, blob := range blobs {
if i == 0 {
res.WriteString("Blobs\n")
}
res.WriteString(fmt.Sprintf(" Index: %d\n", blob.Index))
res.WriteString(fmt.Sprintf(" KZG commitment: %s\n", body.BlobKZGCommitments[i].String()))
}
return res.String(), nil
}
func outputBellatrixBlockExecutionPayload(_ context.Context,
verbose bool,
payload *bellatrix.ExecutionPayload,

View File

@@ -31,6 +31,7 @@ import (
"github.com/attestantio/go-eth2-client/spec/bellatrix"
"github.com/attestantio/go-eth2-client/spec/capella"
"github.com/attestantio/go-eth2-client/spec/deneb"
"github.com/attestantio/go-eth2-client/spec/electra"
"github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/pkg/errors"
standardchaintime "github.com/wealdtech/ethdo/services/chaintime/standard"
@@ -124,6 +125,17 @@ func process(ctx context.Context, data *dataIn) (*dataOut, error) {
if err := outputDenebBlock(ctx, data.jsonOutput, data.sszOutput, block.Deneb, blobSidecars); err != nil {
return nil, errors.Wrap(err, "failed to output block")
}
case spec.DataVersionElectra:
blobSidecarsResponse, err := results.eth2Client.(eth2client.BlobSidecarsProvider).BlobSidecars(ctx, &api.BlobSidecarsOpts{
Block: data.blockID,
})
if err != nil {
return nil, errors.Wrap(err, "failed to obtain blob sidecars")
}
blobSidecars := blobSidecarsResponse.Data
if err := outputElectraBlock(ctx, data.jsonOutput, data.sszOutput, block.Electra, blobSidecars); err != nil {
return nil, errors.Wrap(err, "failed to output block")
}
default:
return nil, errors.New("unknown block version")
}
@@ -187,6 +199,14 @@ func headEventHandler(event *apiv1.Event) {
if err == nil {
err = outputDenebBlock(context.Background(), jsonOutput, sszOutput, block.Deneb, blobSidecarsResponse.Data)
}
case spec.DataVersionElectra:
var blobSidecarsResponse *api.Response[[]*deneb.BlobSidecar]
blobSidecarsResponse, err = results.eth2Client.(eth2client.BlobSidecarsProvider).BlobSidecars(ctx, &api.BlobSidecarsOpts{
Block: blockID,
})
if err == nil {
err = outputElectraBlock(context.Background(), jsonOutput, sszOutput, block.Electra, blobSidecarsResponse.Data)
}
default:
err = errors.New("unknown block version")
}
@@ -319,6 +339,35 @@ func outputDenebBlock(ctx context.Context,
return nil
}
func outputElectraBlock(ctx context.Context,
jsonOutput bool,
sszOutput bool,
signedBlock *electra.SignedBeaconBlock,
blobs []*deneb.BlobSidecar,
) error {
switch {
case jsonOutput:
data, err := json.Marshal(signedBlock)
if err != nil {
return errors.Wrap(err, "failed to generate JSON")
}
fmt.Printf("%s\n", string(data))
case sszOutput:
data, err := signedBlock.MarshalSSZ()
if err != nil {
return errors.Wrap(err, "failed to generate SSZ")
}
fmt.Printf("%x\n", data)
default:
data, err := outputElectraBlockText(ctx, results, signedBlock, blobs)
if err != nil {
return errors.Wrap(err, "failed to generate text")
}
fmt.Print(data)
}
return nil
}
func timeToBlockID(ctx context.Context, eth2Client eth2client.Service, input string) (string, error) {
var timestamp time.Time

View File

@@ -34,6 +34,7 @@ type command struct {
timeout time.Duration
connection string
allowInsecureConnections bool
customSpecSupport bool
// Input.
xepoch string
@@ -64,10 +65,11 @@ type vote struct {
func newCommand(_ context.Context) (*command, error) {
c := &command{
quiet: viper.GetBool("quiet"),
verbose: viper.GetBool("verbose"),
debug: viper.GetBool("debug"),
json: viper.GetBool("json"),
quiet: viper.GetBool("quiet"),
verbose: viper.GetBool("verbose"),
debug: viper.GetBool("debug"),
json: viper.GetBool("json"),
customSpecSupport: viper.GetBool("custom-spec"),
}
// Timeout.

View File

@@ -97,6 +97,9 @@ func (c *command) process(ctx context.Context) error {
case spec.DataVersionDeneb:
c.incumbent = state.Deneb.ETH1Data
c.eth1DataVotes = state.Deneb.ETH1DataVotes
case spec.DataVersionElectra:
c.incumbent = state.Electra.ETH1Data
c.eth1DataVotes = state.Electra.ETH1DataVotes
default:
return fmt.Errorf("unhandled beacon state version %v", state.Version)
}
@@ -124,10 +127,11 @@ func (c *command) setup(ctx context.Context) error {
// Connect to the client.
c.eth2Client, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
LogFallback: !c.quiet,
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
CustomSpecSupport: c.customSpecSupport,
LogFallback: !c.quiet,
})
if err != nil {
return errors.Wrap(err, "failed to connect to beacon node")

View File

@@ -33,6 +33,7 @@ type command struct {
timeout time.Duration
connection string
allowInsecureConnections bool
customSpecSupport bool
// Input.
epoch string
@@ -49,10 +50,11 @@ type command struct {
func newCommand(_ context.Context) (*command, error) {
c := &command{
quiet: viper.GetBool("quiet"),
verbose: viper.GetBool("verbose"),
debug: viper.GetBool("debug"),
json: viper.GetBool("json"),
quiet: viper.GetBool("quiet"),
verbose: viper.GetBool("verbose"),
debug: viper.GetBool("debug"),
json: viper.GetBool("json"),
customSpecSupport: viper.GetBool("custom-spec"),
}
// Timeout.

View File

@@ -62,10 +62,11 @@ func (c *command) setup(ctx context.Context) error {
// Connect to the client.
c.eth2Client, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
LogFallback: !c.quiet,
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
CustomSpecSupport: c.customSpecSupport,
LogFallback: !c.quiet,
})
if err != nil {
return errors.Wrap(err, "failed to connect to beacon node")

View File

@@ -31,6 +31,7 @@ type dataIn struct {
// Input
connection string
allowInsecureConnections bool
customSpecSupport bool
timestamp string
slot string
epoch string
@@ -73,6 +74,7 @@ func input(_ context.Context) (*dataIn, error) {
data.connection = viper.GetString("connection")
data.allowInsecureConnections = viper.GetBool("allow-insecure-connections")
data.customSpecSupport = viper.GetBool("custom-spec")
return data, nil
}

View File

@@ -31,10 +31,11 @@ func process(ctx context.Context, data *dataIn) (*dataOut, error) {
}
eth2Client, err := util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: data.connection,
Timeout: data.timeout,
AllowInsecure: data.allowInsecureConnections,
LogFallback: !data.quiet,
Address: data.connection,
Timeout: data.timeout,
AllowInsecure: data.allowInsecureConnections,
CustomSpecSupport: data.customSpecSupport,
LogFallback: !data.quiet,
})
if err != nil {
return nil, errors.Wrap(err, "failed to connect to Ethereum 2 beacon node")

View File

@@ -33,6 +33,7 @@ type command struct {
timeout time.Duration
connection string
allowInsecureConnections bool
customSpecSupport bool
// Input.
data string
@@ -60,9 +61,10 @@ type command struct {
func newCommand(_ context.Context) (*command, error) {
c := &command{
quiet: viper.GetBool("quiet"),
verbose: viper.GetBool("verbose"),
debug: viper.GetBool("debug"),
quiet: viper.GetBool("quiet"),
verbose: viper.GetBool("verbose"),
debug: viper.GetBool("debug"),
customSpecSupport: viper.GetBool("custom-spec"),
}
// Timeout.

View File

@@ -87,10 +87,11 @@ func (c *command) setup(ctx context.Context) error {
// Connect to the client.
c.eth2Client, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
LogFallback: !c.quiet,
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
CustomSpecSupport: c.customSpecSupport,
LogFallback: !c.quiet,
})
if err != nil {
return errors.Wrap(err, "failed to connect to beacon node")

View File

@@ -40,10 +40,11 @@ In quiet mode this will return 0 if the chain information can be obtained, other
ctx := context.Background()
eth2Client, err := util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
LogFallback: !viper.GetBool("quiet"),
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
CustomSpecSupport: viper.GetBool("custom-spec"),
LogFallback: !viper.GetBool("quiet"),
})
errCheck(err, "Failed to connect to Ethereum 2 beacon node")

View File

@@ -41,10 +41,11 @@ In quiet mode this will return 0 if the chain specification can be obtained, oth
ctx := context.Background()
eth2Client, err := util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
LogFallback: !viper.GetBool("quiet"),
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
CustomSpecSupport: viper.GetBool("custom-spec"),
LogFallback: !viper.GetBool("quiet"),
})
errCheck(err, "Failed to connect to Ethereum consensus node")

View File

@@ -44,10 +44,11 @@ In quiet mode this will return 0 if the chain status can be obtained, otherwise
ctx := context.Background()
eth2Client, err := util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
LogFallback: !viper.GetBool("quiet"),
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
CustomSpecSupport: viper.GetBool("custom-spec"),
LogFallback: !viper.GetBool("quiet"),
})
errCheck(err, "Failed to connect to Ethereum 2 beacon node")

View File

@@ -34,6 +34,7 @@ type command struct {
timeout time.Duration
connection string
allowInsecureConnections bool
customSpecSupport bool
// Operation.
epoch string
@@ -108,6 +109,7 @@ func newCommand(_ context.Context) (*command, error) {
c.connection = viper.GetString("connection")
c.allowInsecureConnections = viper.GetBool("allow-insecure-connections")
c.customSpecSupport = viper.GetBool("custom-spec")
c.epoch = viper.GetString("epoch")
c.stream = viper.GetBool("stream")

View File

@@ -184,17 +184,21 @@ func (c *command) processSlots(ctx context.Context,
return 0, 0, 0, 0, 0, 0, nil, nil, err
}
for _, attestation := range attestations {
if attestation.Data.Slot < c.chainTime.FirstSlotOfEpoch(c.summary.Epoch) || attestation.Data.Slot >= c.chainTime.FirstSlotOfEpoch(c.summary.Epoch+1) {
attestationData, err := attestation.Data()
if err != nil {
return 0, 0, 0, 0, 0, 0, nil, nil, errors.Wrap(err, fmt.Sprintf("failed to obtain attestation data for slot %d", slot))
}
if attestationData.Slot < c.chainTime.FirstSlotOfEpoch(c.summary.Epoch) || attestationData.Slot >= c.chainTime.FirstSlotOfEpoch(c.summary.Epoch+1) {
// Outside of this epoch's range.
continue
}
slotCommittees, exists := allCommittees[attestation.Data.Slot]
slotCommittees, exists := allCommittees[attestationData.Slot]
if !exists {
response, err := c.beaconCommitteesProvider.BeaconCommittees(ctx, &api.BeaconCommitteesOpts{
State: fmt.Sprintf("%d", attestation.Data.Slot),
State: fmt.Sprintf("%d", attestationData.Slot),
})
if err != nil {
return 0, 0, 0, 0, 0, 0, nil, nil, errors.Wrap(err, fmt.Sprintf("failed to obtain committees for slot %d", attestation.Data.Slot))
return 0, 0, 0, 0, 0, 0, nil, nil, errors.Wrap(err, fmt.Sprintf("failed to obtain committees for slot %d", attestationData.Slot))
}
for _, beaconCommittee := range response.Data {
if _, exists := allCommittees[beaconCommittee.Slot]; !exists {
@@ -209,22 +213,26 @@ 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
headCorrect, err := util.AttestationHeadCorrect(ctx, headersCache, attestation)
inclusionDistance := slot - attestationData.Slot
headCorrect, err := util.AttestationHeadCorrect(ctx, headersCache, attestationData)
if err != nil {
return 0, 0, 0, 0, 0, 0, nil, nil, err
}
targetCorrect, err := util.AttestationTargetCorrect(ctx, headersCache, c.chainTime, attestation)
targetCorrect, err := util.AttestationTargetCorrect(ctx, headersCache, c.chainTime, attestationData)
if err != nil {
return 0, 0, 0, 0, 0, 0, nil, nil, err
}
for i := uint64(0); i < attestation.AggregationBits.Len(); i++ {
if attestation.AggregationBits.BitAt(i) {
aggregationBits, err := attestation.AggregationBits()
if err != nil {
return 0, 0, 0, 0, 0, 0, nil, nil, err
}
for i := uint64(0); i < aggregationBits.Len(); i++ {
if aggregationBits.BitAt(i) {
votes[committee[int(i)]] = struct{}{}
if _, exists := headCorrects[committee[int(i)]]; !exists && headCorrect {
headCorrects[committee[int(i)]] = struct{}{}
@@ -245,6 +253,7 @@ func (c *command) processSlots(ctx context.Context,
}
}
}
return len(votes),
len(headCorrects),
len(headTimelys),
@@ -331,10 +340,11 @@ func (c *command) setup(ctx context.Context) error {
// Connect to the client.
c.eth2Client, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
LogFallback: !c.quiet,
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
CustomSpecSupport: c.customSpecSupport,
LogFallback: !c.quiet,
})
if err != nil {
return errors.Wrap(err, "failed to connect to beacon node")
@@ -391,6 +401,8 @@ func (c *command) processBlobs(ctx context.Context) error {
// No blobs in these forks.
case spec.DataVersionDeneb:
c.summary.Blobs += len(block.Deneb.Message.Body.BlobKZGCommitments)
case spec.DataVersionElectra:
c.summary.Blobs += len(block.Electra.Message.Body.BlobKZGCommitments)
default:
return fmt.Errorf("unhandled block version %v", block.Version)
}

View File

@@ -47,10 +47,11 @@ In quiet mode this will return 0 if the exit is verified correctly, otherwise 1.
errCheck(err, "Failed to obtain signed operation")
eth2Client, err := util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
LogFallback: !viper.GetBool("quiet"),
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
CustomSpecSupport: viper.GetBool("custom-spec"),
LogFallback: !viper.GetBool("quiet"),
})
errCheck(err, "Failed to connect to Ethereum 2 beacon node")

View File

@@ -51,10 +51,11 @@ func input(ctx context.Context) (*dataIn, error) {
var err error
data.eth2Client, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
LogFallback: !data.quiet,
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
CustomSpecSupport: viper.GetBool("custom-spec"),
LogFallback: !data.quiet,
})
if err != nil {
return nil, err

View File

@@ -37,10 +37,11 @@ In quiet mode this will return 0 if the node information can be obtained, otherw
ctx := context.Background()
eth2Client, err := util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
LogFallback: !viper.GetBool("quiet"),
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
CustomSpecSupport: viper.GetBool("custom-spec"),
LogFallback: !viper.GetBool("quiet"),
})
errCheck(err, "Failed to connect to Ethereum 2 beacon node")

View File

@@ -34,6 +34,7 @@ type command struct {
timeout time.Duration
connection string
allowInsecureConnections bool
customSpecSupport bool
// Operation.
epoch string
@@ -62,6 +63,7 @@ func newCommand(_ context.Context) (*command, error) {
timeout: viper.GetDuration("timeout"),
connection: viper.GetString("connection"),
allowInsecureConnections: viper.GetBool("allow-insecure-connections"),
customSpecSupport: viper.GetBool("custom-spec"),
epoch: viper.GetString("epoch"),
slot: viper.GetString("slot"),
jsonOutput: viper.GetBool("json"),

View File

@@ -88,10 +88,11 @@ func (c *command) setup(ctx context.Context) error {
// Connect to the client.
c.eth2Client, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
LogFallback: !c.quiet,
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
CustomSpecSupport: c.customSpecSupport,
LogFallback: !c.quiet,
})
if err != nil {
return errors.Wrap(err, "failed to connect to beacon node")

View File

@@ -136,6 +136,7 @@ func init() {
addPersistentFlags()
}
//nolint:gocyclo
func addPersistentFlags() {
RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.ethdo.yaml)")
@@ -252,6 +253,10 @@ func addPersistentFlags() {
if err := viper.BindPFlag("allow-insecure-connections", RootCmd.PersistentFlags().Lookup("allow-insecure-connections")); err != nil {
panic(err)
}
RootCmd.PersistentFlags().Bool("custom-spec", false, "allow custom (non-mainnet) beacon node specifications")
if err := viper.BindPFlag("custom-spec", RootCmd.PersistentFlags().Lookup("custom-spec")); err != nil {
panic(err)
}
}
// initConfig reads in config file and ENV variables if set.

View File

@@ -53,10 +53,11 @@ func input(ctx context.Context) (*dataIn, error) {
// Ethereum 2 client.
var err error
data.eth2Client, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
LogFallback: !data.quiet,
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
CustomSpecSupport: viper.GetBool("custom-spec"),
LogFallback: !data.quiet,
})
if err != nil {
return nil, err

View File

@@ -33,6 +33,7 @@ type command struct {
timeout time.Duration
connection string
allowInsecureConnections bool
customSpecSupport bool
// Input.
validator string
@@ -51,9 +52,10 @@ type command struct {
func newCommand(_ context.Context) (*command, error) {
c := &command{
quiet: viper.GetBool("quiet"),
verbose: viper.GetBool("verbose"),
debug: viper.GetBool("debug"),
quiet: viper.GetBool("quiet"),
verbose: viper.GetBool("verbose"),
debug: viper.GetBool("debug"),
customSpecSupport: viper.GetBool("custom-spec"),
}
// Timeout.

View File

@@ -120,6 +120,13 @@ func (c *command) process(ctx context.Context) error {
} else {
c.inclusions = append(c.inclusions, 2)
}
case spec.DataVersionElectra:
aggregate = block.Electra.Message.Body.SyncAggregate
if aggregate.SyncCommitteeBits.BitAt(c.committeeIndex) {
c.inclusions = append(c.inclusions, 1)
} else {
c.inclusions = append(c.inclusions, 2)
}
default:
return fmt.Errorf("unhandled block version %v", block.Version)
}
@@ -134,10 +141,11 @@ func (c *command) setup(ctx context.Context) error {
// Connect to the client.
c.eth2Client, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
LogFallback: !c.quiet,
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
CustomSpecSupport: c.customSpecSupport,
LogFallback: !c.quiet,
})
if err != nil {
return err

View File

@@ -52,10 +52,11 @@ func input(ctx context.Context) (*dataIn, error) {
// Ethereum 2 client.
var err error
data.eth2Client, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
LogFallback: !data.quiet,
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
CustomSpecSupport: viper.GetBool("custom-spec"),
LogFallback: !data.quiet,
})
if err != nil {
return nil, err

View File

@@ -35,6 +35,7 @@ type command struct {
timeout time.Duration
connection string
allowInsecureConnections bool
customSpecSupport bool
// Data access.
consensusClient eth2client.Service
@@ -46,9 +47,10 @@ type command struct {
func newCommand(_ context.Context) (*command, error) {
c := &command{
quiet: viper.GetBool("quiet"),
verbose: viper.GetBool("verbose"),
debug: viper.GetBool("debug"),
quiet: viper.GetBool("quiet"),
verbose: viper.GetBool("verbose"),
debug: viper.GetBool("debug"),
customSpecSupport: viper.GetBool("custom-spec"),
}
// Timeout.

View File

@@ -49,10 +49,11 @@ func (c *command) setup(ctx context.Context) error {
// Connect to the consensus node.
c.consensusClient, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
LogFallback: !c.quiet,
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
CustomSpecSupport: c.customSpecSupport,
LogFallback: !c.quiet,
})
if err != nil {
return errors.Wrap(err, "failed to connect to consensus node")

View File

@@ -54,6 +54,7 @@ type command struct {
timeout time.Duration
connection string
allowInsecureConnections bool
customSpecSupport bool
// Information required to generate the operations.
withdrawalAddress bellatrix.ExecutionAddress
@@ -78,6 +79,7 @@ func newCommand(_ context.Context) (*command, error) {
timeout: viper.GetDuration("timeout"),
connection: viper.GetString("connection"),
allowInsecureConnections: viper.GetBool("allow-insecure-connections"),
customSpecSupport: viper.GetBool("custom-spec"),
prepareOffline: viper.GetBool("prepare-offline"),
account: viper.GetString("account"),
withdrawalAccount: viper.GetString("withdrawal-account"),

View File

@@ -482,17 +482,17 @@ func (c *command) generateOperationFromSeedAndPath(ctx context.Context,
validator, exists := validators[validatorPubkey]
if !exists {
if c.debug {
fmt.Fprintf(os.Stderr, "no validator found with public key %s at path %s\n", validatorPubkey, path)
fmt.Fprintf(os.Stderr, "No validator found with public key %s at path %s\n", validatorPubkey, path)
}
return false, nil
}
if c.verbose {
if c.verbose || c.debug {
fmt.Fprintf(os.Stderr, "Validator %d found with public key %s at path %s\n", validator.Index, validatorPubkey, path)
}
if validator.WithdrawalCredentials[0] != byte(0) {
if c.debug {
if c.verbose || c.debug {
fmt.Fprintf(os.Stderr, "Validator %s has non-BLS withdrawal credentials %#x\n", validatorPubkey, validator.WithdrawalCredentials)
}
return false, nil
@@ -523,7 +523,7 @@ func (c *command) generateOperationFromSeedAndPath(ctx context.Context,
withdrawalCredentials := ethutil.SHA256(withdrawalPubkey)
withdrawalCredentials[0] = byte(0) // BLS_WITHDRAWAL_PREFIX
if !bytes.Equal(withdrawalCredentials, validator.WithdrawalCredentials) {
if c.verbose && c.privateKey == "" {
if (c.verbose || c.debug) && c.privateKey == "" {
fmt.Fprintf(os.Stderr, "Validator %s withdrawal credentials %#x do not match expected credentials, cannot update\n", validatorPubkey, validator.WithdrawalCredentials)
}
return false, nil
@@ -538,6 +538,10 @@ func (c *command) generateOperationFromSeedAndPath(ctx context.Context,
return false, err
}
if c.verbose || c.debug {
fmt.Fprintf(os.Stderr, "Withdrawal credentials change operation generated for validator with public key %s at path %s\n", validatorPubkey, path)
}
return true, nil
}
@@ -727,10 +731,11 @@ func (c *command) setup(ctx context.Context) error {
// Connect to the consensus node.
var err error
c.consensusClient, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
LogFallback: !c.quiet,
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
CustomSpecSupport: c.customSpecSupport,
LogFallback: !c.quiet,
})
if err != nil {
return err

View File

@@ -28,8 +28,9 @@ type dataIn struct {
verbose bool
debug bool
// Ethereum 2 connection.
eth2Client string
allowInsecure bool
eth2Client string
allowInsecure bool
customSpecSupport bool
// Operation.
account string
pubKey string
@@ -50,6 +51,7 @@ func input(_ context.Context) (*dataIn, error) {
// Ethereum 2 connection.
data.eth2Client = viper.GetString("connection")
data.allowInsecure = viper.GetBool("allow-insecure-connections")
data.customSpecSupport = viper.GetBool("custom-spec")
// Account.
data.account = viper.GetString("account")

View File

@@ -32,10 +32,11 @@ func process(ctx context.Context, data *dataIn) (*dataOut, error) {
// Ethereum 2 client.
eth2Client, err := util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: data.eth2Client,
Timeout: data.timeout,
AllowInsecure: data.allowInsecure,
LogFallback: !data.quiet,
Address: data.eth2Client,
Timeout: data.timeout,
AllowInsecure: data.allowInsecure,
CustomSpecSupport: data.customSpecSupport,
LogFallback: !data.quiet,
})
if err != nil {
return nil, err

View File

@@ -50,6 +50,7 @@ type command struct {
timeout time.Duration
connection string
allowInsecureConnections bool
customSpecSupport bool
// Information required to generate the operations.
chainInfo *beacon.ChainInfo
@@ -73,6 +74,7 @@ func newCommand(_ context.Context) (*command, error) {
timeout: viper.GetDuration("timeout"),
connection: viper.GetString("connection"),
allowInsecureConnections: viper.GetBool("allow-insecure-connections"),
customSpecSupport: viper.GetBool("custom-spec"),
prepareOffline: viper.GetBool("prepare-offline"),
passphrases: util.GetPassphrases(),
mnemonic: viper.GetString("mnemonic"),

View File

@@ -595,10 +595,11 @@ func (c *command) setup(ctx context.Context) error {
// Connect to the consensus node.
var err error
c.consensusClient, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
LogFallback: !c.quiet,
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
CustomSpecSupport: c.customSpecSupport,
LogFallback: !c.quiet,
})
if err != nil {
return err

View File

@@ -35,6 +35,7 @@ type command struct {
timeout time.Duration
connection string
allowInsecureConnections bool
customSpecSupport bool
// Input.
validators int64

View File

@@ -125,10 +125,11 @@ func (c *command) setup(ctx context.Context) error {
// Connect to the client.
c.eth2Client, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
LogFallback: !c.quiet,
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
CustomSpecSupport: c.customSpecSupport,
LogFallback: !c.quiet,
})
if err != nil {
return errors.Wrap(err, "failed to connect to beacon node")

View File

@@ -34,6 +34,7 @@ type command struct {
timeout time.Duration
connection string
allowInsecureConnections bool
customSpecSupport bool
// Operation.
epoch string
@@ -131,6 +132,7 @@ func newCommand(_ context.Context) (*command, error) {
c.connection = viper.GetString("connection")
c.allowInsecureConnections = viper.GetBool("allow-insecure-connections")
c.customSpecSupport = viper.GetBool("custom-spec")
c.epoch = viper.GetString("epoch")
c.validators = viper.GetStringSlice("validators")

View File

@@ -243,16 +243,24 @@ func (c *command) processAttesterDutiesSlot(ctx context.Context,
return err
}
for _, attestation := range attestations {
if _, exists := dutiesBySlot[attestation.Data.Slot]; !exists {
attestationData, err := attestation.Data()
if err != nil {
continue
}
aggregationBits, err := attestation.AggregationBits()
if err != nil {
continue
}
if _, exists := dutiesBySlot[attestationData.Slot]; !exists {
// We do not have any attestations for this slot.
continue
}
if _, exists := dutiesBySlot[attestation.Data.Slot][attestation.Data.Index]; !exists {
if _, exists := dutiesBySlot[attestationData.Slot][attestationData.Index]; !exists {
// We do not have any attestations for this committee.
continue
}
for _, duty := range dutiesBySlot[attestation.Data.Slot][attestation.Data.Index] {
if attestation.AggregationBits.BitAt(duty.ValidatorCommitteeIndex) {
for _, duty := range dutiesBySlot[attestationData.Slot][attestationData.Index] {
if aggregationBits.BitAt(duty.ValidatorCommitteeIndex) {
// Found it.
if _, exists := votes[duty.ValidatorIndex]; exists {
// Duplicate; ignore.
@@ -261,17 +269,17 @@ func (c *command) processAttesterDutiesSlot(ctx context.Context,
votes[duty.ValidatorIndex] = struct{}{}
// Update the metrics for the attestation.
index := int(attestation.Data.Slot - c.chainTime.FirstSlotOfEpoch(c.summary.Epoch))
index := int(attestationData.Slot - c.chainTime.FirstSlotOfEpoch(c.summary.Epoch))
c.summary.Slots[index].Attestations.Included++
inclusionDelay := slot - duty.Slot
fault := &validatorFault{
Validator: duty.ValidatorIndex,
AttestationData: attestation.Data,
AttestationData: attestationData,
InclusionDistance: int(inclusionDelay),
}
headCorrect, err := util.AttestationHeadCorrect(ctx, headersCache, attestation)
headCorrect, err := util.AttestationHeadCorrect(ctx, headersCache, attestationData)
if err != nil {
return errors.Wrap(err, "failed to calculate if attestation had correct head vote")
}
@@ -295,7 +303,7 @@ func (c *command) processAttesterDutiesSlot(ctx context.Context,
c.summary.UntimelySourceValidators = append(c.summary.UntimelySourceValidators, fault)
}
targetCorrect, err := util.AttestationTargetCorrect(ctx, headersCache, c.chainTime, attestation)
targetCorrect, err := util.AttestationTargetCorrect(ctx, headersCache, c.chainTime, attestationData)
if err != nil {
return errors.Wrap(err, "failed to calculate if attestation had correct target vote")
}
@@ -399,10 +407,11 @@ func (c *command) setup(ctx context.Context) error {
// Connect to the client.
c.eth2Client, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
LogFallback: !c.quiet,
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
CustomSpecSupport: c.customSpecSupport,
LogFallback: !c.quiet,
})
if err != nil {
return errors.Wrap(err, "failed to connect to beacon node")

View File

@@ -39,6 +39,7 @@ type command struct {
timeout time.Duration
connection string
allowInsecureConnections bool
customSpecSupport bool
// Processing.
consensusClient consensusclient.Service
@@ -60,6 +61,7 @@ func newCommand(_ context.Context) (*command, error) {
timeout: viper.GetDuration("timeout"),
connection: viper.GetString("connection"),
allowInsecureConnections: viper.GetBool("allow-insecure-connections"),
customSpecSupport: viper.GetBool("custom-spec"),
validator: viper.GetString("validator"),
res: &res{},
}

View File

@@ -129,10 +129,11 @@ func (c *command) setup(ctx context.Context) error {
// Connect to the consensus node.
var err error
c.consensusClient, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
LogFallback: !c.quiet,
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
CustomSpecSupport: c.customSpecSupport,
LogFallback: !c.quiet,
})
if err != nil {
return err

View File

@@ -33,6 +33,7 @@ type command struct {
timeout time.Duration
connection string
allowInsecureConnections bool
customSpecSupport bool
// Input.
validators string
@@ -76,6 +77,7 @@ func newCommand(_ context.Context) (*command, error) {
c.connection = viper.GetString("connection")
c.allowInsecureConnections = viper.GetBool("allow-insecure-connections")
c.customSpecSupport = viper.GetBool("custom-spec")
c.validators = viper.GetString("validators")

View File

@@ -114,10 +114,11 @@ func (c *command) setup(ctx context.Context) error {
// Connect to the client.
c.eth2Client, err = util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
LogFallback: !c.quiet,
Address: c.connection,
Timeout: c.timeout,
AllowInsecure: c.allowInsecureConnections,
CustomSpecSupport: c.customSpecSupport,
LogFallback: !c.quiet,
})
if err != nil {
return errors.Wrap(err, "failed to connect to beacon node")

View File

@@ -47,10 +47,11 @@ In quiet mode this will return 0 if the validator information can be obtained, o
ctx := context.Background()
eth2Client, err := util.ConnectToBeaconNode(ctx, &util.ConnectOpts{
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
LogFallback: !viper.GetBool("quiet"),
Address: viper.GetString("connection"),
Timeout: viper.GetDuration("timeout"),
AllowInsecure: viper.GetBool("allow-insecure-connections"),
CustomSpecSupport: viper.GetBool("custom-spec"),
LogFallback: !viper.GetBool("quiet"),
})
errCheck(err, "Failed to connect to Ethereum 2 beacon node")

View File

@@ -24,7 +24,7 @@ import (
// ReleaseVersion is the release version of the codebase.
// Usually overridden by tag names when building binaries.
var ReleaseVersion = "local build (latest release 1.35.2)"
var ReleaseVersion = "local build (latest release 1.35.3)"
// versionCmd represents the version command.
var versionCmd = &cobra.Command{

12
go.mod
View File

@@ -5,7 +5,7 @@ go 1.21
toolchain go1.21.6
require (
github.com/attestantio/go-eth2-client v0.20.0
github.com/attestantio/go-eth2-client v0.21.4-0.20240514220516-2646ce28c3e4
github.com/ferranbt/fastssz v0.1.3
github.com/gofrs/uuid v4.4.0+incompatible
github.com/google/uuid v1.6.0
@@ -14,7 +14,7 @@ require (
github.com/mitchellh/go-homedir v1.1.0
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354
github.com/pkg/errors v0.9.1
github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7
github.com/prysmaticlabs/go-bitfield v0.0.0-20240328144219-a1caa50c3a1e
github.com/prysmaticlabs/go-ssz v0.0.0-20210121151755-f6208871c388
github.com/rs/zerolog v1.32.0
github.com/shopspring/decimal v1.3.1
@@ -38,7 +38,7 @@ require (
github.com/wealdtech/go-eth2-wallet-store-scratch v1.7.2
github.com/wealdtech/go-eth2-wallet-types/v2 v2.11.0
github.com/wealdtech/go-string2eth v1.2.1
golang.org/x/text v0.14.0
golang.org/x/text v0.15.0
)
require (
@@ -69,6 +69,7 @@ require (
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/pk910/dynamic-ssz v0.0.4 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.19.0 // indirect
github.com/prometheus/client_model v0.6.0 // indirect
@@ -90,16 +91,17 @@ require (
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c // indirect
google.golang.org/grpc v1.62.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/Knetic/govaluate.v3 v3.0.0 // indirect
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect

28
go.sum
View File

@@ -1,5 +1,7 @@
github.com/attestantio/go-eth2-client v0.20.0 h1:5UA/qkABDasmdiZtaqK60sEdFw0vUGQU2c0NS/0IhfU=
github.com/attestantio/go-eth2-client v0.20.0/go.mod h1:Tb412NpzhsC0sbtpXS4D51y5se6nDkWAi6amsJrqX9c=
github.com/attestantio/go-eth2-client v0.21.4-0.20240514220516-2646ce28c3e4 h1:tvLDLWLMMdo/UzMPCX+a9wh+zdAZzS2NEOqAaq9m5BY=
github.com/attestantio/go-eth2-client v0.21.4-0.20240514220516-2646ce28c3e4/go.mod h1:RssbJ8txdfZ+O7cIjOKCvizcTWHzgPuJNByrwtQ6tBQ=
github.com/attestantio/go-eth2-client v0.21.4 h1:1QW4f3NXCcbUsxmRBElotTjSIhRwLsmdowUvxJnyaJU=
github.com/attestantio/go-eth2-client v0.21.4/go.mod h1:d7ZPNrMX8jLfIgML5u7QZxFo2AukLM+5m08iMaLdqb8=
github.com/aws/aws-sdk-go v1.51.1 h1:AFvTihcDPanvptoKS09a4yYmNtPm3+pXlk6uYHmZiFk=
github.com/aws/aws-sdk-go v1.51.1/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -109,6 +111,10 @@ github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6Fx
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pk910/dynamic-ssz v0.0.3 h1:fCWzFowq9P6SYCc7NtJMkZcIHk+r5hSVD+32zVi6Aio=
github.com/pk910/dynamic-ssz v0.0.3/go.mod h1:b6CrLaB2X7pYA+OSEEbkgXDEcRnjLOZIxZTsMuO/Y9c=
github.com/pk910/dynamic-ssz v0.0.4 h1:DT29+1055tCEPCaR4V/ez+MOKW7BzBsmjyFvBRqx0ME=
github.com/pk910/dynamic-ssz v0.0.4/go.mod h1:b6CrLaB2X7pYA+OSEEbkgXDEcRnjLOZIxZTsMuO/Y9c=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -124,8 +130,8 @@ github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGK
github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g=
github.com/protolambda/zssz v0.1.5 h1:7fjJjissZIIaa2QcvmhS/pZISMX21zVITt49sW1ouek=
github.com/protolambda/zssz v0.1.5/go.mod h1:a4iwOX5FE7/JkKA+J/PH0Mjo9oXftN6P8NZyL28gpag=
github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7 h1:0tVE4tdWQK9ZpYygoV7+vS6QkDvQVySboMVEIxBJmXw=
github.com/prysmaticlabs/go-bitfield v0.0.0-20210809151128-385d8c5e3fb7/go.mod h1:wmuf/mdK4VMD+jA9ThwcUKjg3a2XWM9cVfFYjDyY4j4=
github.com/prysmaticlabs/go-bitfield v0.0.0-20240328144219-a1caa50c3a1e h1:ATgOe+abbzfx9kCPeXIW4fiWyDdxlwHw07j8UGhdTd4=
github.com/prysmaticlabs/go-bitfield v0.0.0-20240328144219-a1caa50c3a1e/go.mod h1:wmuf/mdK4VMD+jA9ThwcUKjg3a2XWM9cVfFYjDyY4j4=
github.com/prysmaticlabs/go-ssz v0.0.0-20210121151755-f6208871c388 h1:4bD+ujqGfY4zoDUF3q9MhdmpPXzdp03DYUIlXeQ72kk=
github.com/prysmaticlabs/go-ssz v0.0.0-20210121151755-f6208871c388/go.mod h1:VecIJZrewdAuhVckySLFt2wAAHRME934bSDurP8ftkc=
github.com/r3labs/sse/v2 v2.10.0 h1:hFEkLLFY4LDifoHdiCN/LlGBAdVJYsANaLqNYa1l/v0=
@@ -220,8 +226,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f h1:3CW0unweImhOzd5FmYuRsD4Y4oQFKZIjAnKbjV4WIrw=
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -240,12 +246,12 @@ golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
@@ -258,6 +264,8 @@ google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/Knetic/govaluate.v3 v3.0.0 h1:18mUyIt4ZlRlFZAAfVetz4/rzlJs9yhN+U02F4u1AOc=
gopkg.in/Knetic/govaluate.v3 v3.0.0/go.mod h1:csKLBORsPbafmSCGTEh3U7Ozmsuq8ZSIlKk1bcqph0E=
gopkg.in/cenkalti/backoff.v1 v1.1.0 h1:Arh75ttbsvlpVA7WtVpH4u9h6Zl46xuptxqLxPiSo4Y=
gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UDAkHu8BrjI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -24,12 +24,12 @@ import (
// AttestationHeadCorrect returns true if the given attestation had the correct head.
func AttestationHeadCorrect(ctx context.Context,
headersCache *BeaconBlockHeaderCache,
attestation *phase0.Attestation,
attestationData *phase0.AttestationData,
) (
bool,
error,
) {
slot := attestation.Data.Slot
slot := attestationData.Slot
for {
header, err := headersCache.Fetch(ctx, slot)
if err != nil {
@@ -45,7 +45,7 @@ func AttestationHeadCorrect(ctx context.Context,
slot--
continue
}
return bytes.Equal(header.Root[:], attestation.Data.BeaconBlockRoot[:]), nil
return bytes.Equal(header.Root[:], attestationData.BeaconBlockRoot[:]), nil
}
}
@@ -53,13 +53,13 @@ func AttestationHeadCorrect(ctx context.Context,
func AttestationTargetCorrect(ctx context.Context,
headersCache *BeaconBlockHeaderCache,
chainTime chaintime.Service,
attestation *phase0.Attestation,
attestationData *phase0.AttestationData,
) (
bool,
error,
) {
// Start with first slot of the target epoch.
slot := chainTime.FirstSlotOfEpoch(attestation.Data.Target.Epoch)
slot := chainTime.FirstSlotOfEpoch(attestationData.Target.Epoch)
for {
header, err := headersCache.Fetch(ctx, slot)
if err != nil {
@@ -75,6 +75,6 @@ func AttestationTargetCorrect(ctx context.Context,
slot--
continue
}
return bytes.Equal(header.Root[:], attestation.Data.Target.Root[:]), nil
return bytes.Equal(header.Root[:], attestationData.Target.Root[:]), nil
}
}

View File

@@ -38,10 +38,11 @@ var defaultBeaconNodeAddresses = []string{
var fallbackBeaconNode = "http://mainnet-consensus.attestant.io/"
type ConnectOpts struct {
Address string
Timeout time.Duration
AllowInsecure bool
LogFallback bool
Address string
Timeout time.Duration
AllowInsecure bool
LogFallback bool
CustomSpecSupport bool
}
// ConnectToBeaconNode connects to a beacon node at the given address.
@@ -56,12 +57,12 @@ func ConnectToBeaconNode(ctx context.Context, opts *ConnectOpts) (eth2client.Ser
if opts.Address != "" {
// We have an explicit address; use it.
return connectToBeaconNode(ctx, opts.Address, opts.Timeout, opts.AllowInsecure)
return connectToBeaconNode(ctx, opts.Address, opts.Timeout, opts.AllowInsecure, opts.CustomSpecSupport)
}
// Try the defaults.
for _, address := range defaultBeaconNodeAddresses {
client, err := connectToBeaconNode(ctx, address, opts.Timeout, opts.AllowInsecure)
client, err := connectToBeaconNode(ctx, address, opts.Timeout, opts.AllowInsecure, opts.CustomSpecSupport)
if err == nil {
return client, nil
}
@@ -71,7 +72,7 @@ func ConnectToBeaconNode(ctx context.Context, opts *ConnectOpts) (eth2client.Ser
if opts.LogFallback {
fmt.Fprintf(os.Stderr, "No connection supplied with --connection parameter and no local beacon node found, attempting to use mainnet fallback\n")
}
client, err := connectToBeaconNode(ctx, fallbackBeaconNode, opts.Timeout, true)
client, err := connectToBeaconNode(ctx, fallbackBeaconNode, opts.Timeout, true, opts.CustomSpecSupport)
if err == nil {
return client, nil
}
@@ -79,7 +80,7 @@ func ConnectToBeaconNode(ctx context.Context, opts *ConnectOpts) (eth2client.Ser
return nil, errors.New("failed to connect to any beacon node")
}
func connectToBeaconNode(ctx context.Context, address string, timeout time.Duration, allowInsecure bool) (eth2client.Service, error) {
func connectToBeaconNode(ctx context.Context, address string, timeout time.Duration, allowInsecure bool, withCustomSpecSupport bool) (eth2client.Service, error) {
if !strings.HasPrefix(address, "http") {
address = fmt.Sprintf("http://%s", address)
}
@@ -101,6 +102,7 @@ func connectToBeaconNode(ctx context.Context, address string, timeout time.Durat
http.WithLogLevel(zerolog.Disabled),
http.WithAddress(address),
http.WithTimeout(timeout),
http.WithCustomSpecSupport(withCustomSpecSupport),
)
if err != nil {
return nil, errors.Wrap(err, "failed to connect to beacon node")