mirror of
https://github.com/wealdtech/ethdo.git
synced 2026-01-09 22:18:01 -05:00
Add slot option for proposer duties.
This commit is contained in:
@@ -1,3 +1,6 @@
|
|||||||
|
dev:
|
||||||
|
- add "slot" to "proposer duties" command
|
||||||
|
|
||||||
1.33.0:
|
1.33.0:
|
||||||
- show all slots with 'synccommittee inclusion'
|
- show all slots with 'synccommittee inclusion'
|
||||||
- add "wallet batch" command
|
- add "wallet batch" command
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright © 2022 Weald Technology Trading.
|
// Copyright © 2022, 2023 Weald Technology Trading.
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// You may obtain a copy of the License at
|
||||||
@@ -37,6 +37,7 @@ type command struct {
|
|||||||
|
|
||||||
// Operation.
|
// Operation.
|
||||||
epoch string
|
epoch string
|
||||||
|
slot string
|
||||||
jsonOutput bool
|
jsonOutput bool
|
||||||
|
|
||||||
// Data access.
|
// Data access.
|
||||||
@@ -55,23 +56,22 @@ type results struct {
|
|||||||
|
|
||||||
func newCommand(_ context.Context) (*command, error) {
|
func newCommand(_ context.Context) (*command, error) {
|
||||||
c := &command{
|
c := &command{
|
||||||
quiet: viper.GetBool("quiet"),
|
quiet: viper.GetBool("quiet"),
|
||||||
verbose: viper.GetBool("verbose"),
|
verbose: viper.GetBool("verbose"),
|
||||||
debug: viper.GetBool("debug"),
|
debug: viper.GetBool("debug"),
|
||||||
results: &results{},
|
timeout: viper.GetDuration("timeout"),
|
||||||
|
connection: viper.GetString("connection"),
|
||||||
|
allowInsecureConnections: viper.GetBool("allow-insecure-connections"),
|
||||||
|
epoch: viper.GetString("epoch"),
|
||||||
|
slot: viper.GetString("slot"),
|
||||||
|
jsonOutput: viper.GetBool("json"),
|
||||||
|
results: &results{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timeout.
|
// Timeout.
|
||||||
if viper.GetDuration("timeout") == 0 {
|
if c.timeout == 0 {
|
||||||
return nil, errors.New("timeout is required")
|
return nil, errors.New("timeout is required")
|
||||||
}
|
}
|
||||||
c.timeout = viper.GetDuration("timeout")
|
|
||||||
|
|
||||||
c.connection = viper.GetString("connection")
|
|
||||||
c.allowInsecureConnections = viper.GetBool("allow-insecure-connections")
|
|
||||||
|
|
||||||
c.epoch = viper.GetString("epoch")
|
|
||||||
c.jsonOutput = viper.GetBool("json")
|
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright © 2022 Weald Technology Trading.
|
// Copyright © 2022, 2023 Weald Technology Trading.
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// You may obtain a copy of the License at
|
||||||
@@ -43,19 +43,32 @@ func (c *command) outputJSON(_ context.Context) (string, error) {
|
|||||||
func (c *command) outputTxt(_ context.Context) (string, error) {
|
func (c *command) outputTxt(_ context.Context) (string, error) {
|
||||||
builder := strings.Builder{}
|
builder := strings.Builder{}
|
||||||
|
|
||||||
builder.WriteString("Epoch ")
|
if len(c.results.Duties) == 1 {
|
||||||
builder.WriteString(fmt.Sprintf("%d:\n", c.results.Epoch))
|
// Only have a single slot, just print the validator.
|
||||||
|
duty := c.results.Duties[0]
|
||||||
for _, duty := range c.results.Duties {
|
builder.WriteString("Validator ")
|
||||||
builder.WriteString(" Slot ")
|
|
||||||
builder.WriteString(fmt.Sprintf("%d: ", duty.Slot))
|
|
||||||
builder.WriteString("validator ")
|
|
||||||
builder.WriteString(fmt.Sprintf("%d", duty.ValidatorIndex))
|
builder.WriteString(fmt.Sprintf("%d", duty.ValidatorIndex))
|
||||||
if c.verbose {
|
if c.verbose {
|
||||||
builder.WriteString(" (pubkey ")
|
builder.WriteString(" (pubkey ")
|
||||||
builder.WriteString(fmt.Sprintf("%#x)", duty.PubKey))
|
builder.WriteString(fmt.Sprintf("%#x)", duty.PubKey))
|
||||||
}
|
}
|
||||||
builder.WriteString("\n")
|
builder.WriteString("\n")
|
||||||
|
} else {
|
||||||
|
// Have multiple slots, print per-slot information.
|
||||||
|
builder.WriteString("Epoch ")
|
||||||
|
builder.WriteString(fmt.Sprintf("%d:\n", c.results.Epoch))
|
||||||
|
|
||||||
|
for _, duty := range c.results.Duties {
|
||||||
|
builder.WriteString(" Slot ")
|
||||||
|
builder.WriteString(fmt.Sprintf("%d: ", duty.Slot))
|
||||||
|
builder.WriteString("validator ")
|
||||||
|
builder.WriteString(fmt.Sprintf("%d", duty.ValidatorIndex))
|
||||||
|
if c.verbose {
|
||||||
|
builder.WriteString(" (pubkey ")
|
||||||
|
builder.WriteString(fmt.Sprintf("%#x)", duty.PubKey))
|
||||||
|
}
|
||||||
|
builder.WriteString("\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.TrimSuffix(builder.String(), "\n"), nil
|
return strings.TrimSuffix(builder.String(), "\n"), nil
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright © 2022 Weald Technology Trading.
|
// Copyright © 2022, 2023 Weald Technology Trading.
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// You may obtain a copy of the License at
|
||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
eth2client "github.com/attestantio/go-eth2-client"
|
eth2client "github.com/attestantio/go-eth2-client"
|
||||||
|
apiv1 "github.com/attestantio/go-eth2-client/api/v1"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
standardchaintime "github.com/wealdtech/ethdo/services/chaintime/standard"
|
standardchaintime "github.com/wealdtech/ethdo/services/chaintime/standard"
|
||||||
"github.com/wealdtech/ethdo/util"
|
"github.com/wealdtech/ethdo/util"
|
||||||
@@ -24,11 +25,45 @@ import (
|
|||||||
|
|
||||||
func (c *command) process(ctx context.Context) error {
|
func (c *command) process(ctx context.Context) error {
|
||||||
// Obtain information we need to process.
|
// Obtain information we need to process.
|
||||||
err := c.setup(ctx)
|
if err := c.setup(ctx); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.slot != "" {
|
||||||
|
return c.processSlot(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.processEpoch(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *command) processSlot(ctx context.Context) error {
|
||||||
|
var err error
|
||||||
|
slot, err := util.ParseSlot(ctx, c.chainTime, c.slot)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to parse slot")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.results.Epoch = c.chainTime.SlotToEpoch(slot)
|
||||||
|
|
||||||
|
duties, err := c.proposerDutiesProvider.ProposerDuties(ctx, c.results.Epoch, nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to obtain proposer duties")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.results.Duties = make([]*apiv1.ProposerDuty, 0, 1)
|
||||||
|
|
||||||
|
for _, duty := range duties {
|
||||||
|
if duty.Slot == slot {
|
||||||
|
c.results.Duties = append(c.results.Duties, duty)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *command) processEpoch(ctx context.Context) error {
|
||||||
|
var err error
|
||||||
c.results.Epoch, err = util.ParseEpoch(ctx, c.chainTime, c.epoch)
|
c.results.Epoch, err = util.ParseEpoch(ctx, c.chainTime, c.epoch)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to parse epoch")
|
return errors.Wrap(err, "failed to parse epoch")
|
||||||
|
|||||||
@@ -48,10 +48,14 @@ func init() {
|
|||||||
proposerCmd.AddCommand(proposerDutiesCmd)
|
proposerCmd.AddCommand(proposerDutiesCmd)
|
||||||
proposerFlags(proposerDutiesCmd)
|
proposerFlags(proposerDutiesCmd)
|
||||||
proposerDutiesCmd.Flags().String("epoch", "", "the epoch for which to fetch duties")
|
proposerDutiesCmd.Flags().String("epoch", "", "the epoch for which to fetch duties")
|
||||||
|
proposerDutiesCmd.Flags().String("slot", "", "the slot for which to fetch duties")
|
||||||
}
|
}
|
||||||
|
|
||||||
func proposerDutiesBindings(cmd *cobra.Command) {
|
func proposerDutiesBindings(cmd *cobra.Command) {
|
||||||
if err := viper.BindPFlag("epoch", cmd.Flags().Lookup("epoch")); err != nil {
|
if err := viper.BindPFlag("epoch", cmd.Flags().Lookup("epoch")); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
if err := viper.BindPFlag("slot", cmd.Flags().Lookup("slot")); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -795,6 +795,7 @@ Proposer commands focus on Ethereum consensus validators' actions as proposers.
|
|||||||
`ethdo proposer duties` provides information on the proposal duties for a given epoch. Options include:
|
`ethdo proposer duties` provides information on the proposal duties for a given epoch. Options include:
|
||||||
|
|
||||||
- `epoch` the epoch in which to obtain the duties (defaults to current epoch)
|
- `epoch` the epoch in which to obtain the duties (defaults to current epoch)
|
||||||
|
- `slot` the slot in which to obtain the duties (overrides epoch if present)
|
||||||
- `json` obtain detailed information in JSON format
|
- `json` obtain detailed information in JSON format
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
49
util/slot.go
Normal file
49
util/slot.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright © 2023 Weald Technology Trading
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/wealdtech/ethdo/services/chaintime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseSlot parses input to calculate the desired slot.
|
||||||
|
func ParseSlot(_ context.Context, chainTime chaintime.Service, slotStr string) (phase0.Slot, error) {
|
||||||
|
currentSlot := chainTime.CurrentSlot()
|
||||||
|
switch slotStr {
|
||||||
|
case "", "current", "head", "-0":
|
||||||
|
return currentSlot, nil
|
||||||
|
case "last":
|
||||||
|
if currentSlot > 0 {
|
||||||
|
currentSlot--
|
||||||
|
}
|
||||||
|
return currentSlot, nil
|
||||||
|
default:
|
||||||
|
val, err := strconv.ParseInt(slotStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.Wrap(err, "failed to parse slot")
|
||||||
|
}
|
||||||
|
if val >= 0 {
|
||||||
|
return phase0.Slot(val), nil
|
||||||
|
}
|
||||||
|
if phase0.Slot(-val) > currentSlot {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return currentSlot + phase0.Slot(val), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
105
util/slot_test.go
Normal file
105
util/slot_test.go
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
// Copyright © 2023 Weald Technology Trading.
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package util_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/attestantio/go-eth2-client/spec/phase0"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
standardchaintime "github.com/wealdtech/ethdo/services/chaintime/standard"
|
||||||
|
"github.com/wealdtech/ethdo/testing/mock"
|
||||||
|
"github.com/wealdtech/ethdo/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseSlot(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// genesis is 1 day ago.
|
||||||
|
genesisTime := time.Now().AddDate(0, 0, -1)
|
||||||
|
slotDuration := 12 * time.Second
|
||||||
|
slotsPerSlot := uint64(32)
|
||||||
|
epochsPerSyncCommitteePeriod := uint64(256)
|
||||||
|
mockGenesisTimeProvider := mock.NewGenesisTimeProvider(genesisTime)
|
||||||
|
mockSpecProvider := mock.NewSpecProvider(slotDuration, slotsPerSlot, epochsPerSyncCommitteePeriod)
|
||||||
|
chainTime, err := standardchaintime.New(context.Background(),
|
||||||
|
standardchaintime.WithLogLevel(zerolog.Disabled),
|
||||||
|
standardchaintime.WithGenesisTimeProvider(mockGenesisTimeProvider),
|
||||||
|
standardchaintime.WithSpecProvider(mockSpecProvider),
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
err string
|
||||||
|
expected phase0.Slot
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Genesis",
|
||||||
|
input: "0",
|
||||||
|
expected: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid",
|
||||||
|
input: "invalid",
|
||||||
|
err: `failed to parse slot: strconv.ParseInt: parsing "invalid": invalid syntax`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Absolute",
|
||||||
|
input: "15",
|
||||||
|
expected: 15,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Current",
|
||||||
|
input: "current",
|
||||||
|
expected: 7200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Last",
|
||||||
|
input: "last",
|
||||||
|
expected: 7199,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "RelativeZero",
|
||||||
|
input: "-0",
|
||||||
|
expected: 7200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Relative",
|
||||||
|
input: "-5",
|
||||||
|
expected: 7195,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "RelativeFar",
|
||||||
|
input: "-50000",
|
||||||
|
expected: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
slot, err := util.ParseSlot(ctx, chainTime, test.input)
|
||||||
|
if test.err != "" {
|
||||||
|
require.EqualError(t, err, test.err)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, test.expected, slot)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user