mirror of
https://github.com/wealdtech/ethdo.git
synced 2026-01-09 22:18:01 -05:00
Compare commits
3 Commits
update-doc
...
electra-mi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb34776d05 | ||
|
|
e29818340d | ||
|
|
083f484907 |
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -35,6 +35,7 @@ type command struct {
|
||||
timeout time.Duration
|
||||
connection string
|
||||
allowInsecureConnections bool
|
||||
customSpecSupport bool
|
||||
|
||||
// Input.
|
||||
validators int64
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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{},
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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
12
go.mod
@@ -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
28
go.sum
@@ -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=
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user