Skip to content

Commit

Permalink
cmd: exit beacon node custom timeout (#3045)
Browse files Browse the repository at this point in the history
Allow users to specify a custom beacon node HTTP communications timeout.

Defaults to 30 seconds.

category: misc
ticket: none
  • Loading branch information
gsora authored Apr 23, 2024
1 parent 21d34cc commit fff580a
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 73 deletions.
33 changes: 20 additions & 13 deletions cmd/exit.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,18 @@ import (
)

type exitConfig struct {
BeaconNodeURL string
ValidatorPubkey string
PrivateKeyPath string
ValidatorKeysDir string
LockFilePath string
PublishAddress string
ExitEpoch uint64
FetchedExitPath string
PlaintextOutput bool
ExitFromFilePath string
Log log.Config
BeaconNodeURL string
ValidatorPubkey string
PrivateKeyPath string
ValidatorKeysDir string
LockFilePath string
PublishAddress string
ExitEpoch uint64
FetchedExitPath string
PlaintextOutput bool
BeaconNodeTimeout time.Duration
ExitFromFilePath string
Log log.Config
}

func newExitCmd(cmds ...*cobra.Command) *cobra.Command {
Expand All @@ -54,6 +55,7 @@ const (
validatorPubkey
exitEpoch
exitFromFile
beaconNodeTimeout
)

func (ef exitFlag) String() string {
Expand All @@ -74,6 +76,8 @@ func (ef exitFlag) String() string {
return "exit-epoch"
case exitFromFile:
return "exit-from-file"
case beaconNodeTimeout:
return "beacon-node-timeout"
default:
return "unknown"
}
Expand Down Expand Up @@ -105,6 +109,8 @@ func bindExitFlags(cmd *cobra.Command, config *exitConfig, flags []exitCLIFlag)
cmd.Flags().Uint64Var(&config.ExitEpoch, exitEpoch.String(), 162304, "Exit epoch at which the validator will exit, must be the same across all the partial exits.")
case exitFromFile:
cmd.Flags().StringVar(&config.ExitFromFilePath, exitFromFile.String(), "", "Retrieves a signed exit message from a pre-prepared file instead of --publish-address.")
case beaconNodeTimeout:
cmd.Flags().DurationVar(&config.BeaconNodeTimeout, beaconNodeTimeout.String(), 30*time.Second, "Timeout for beacon node HTTP calls.")
}

if f.required {
Expand All @@ -113,9 +119,10 @@ func bindExitFlags(cmd *cobra.Command, config *exitConfig, flags []exitCLIFlag)
}
}

func eth2Client(ctx context.Context, u string) (eth2wrap.Client, error) {
func eth2Client(ctx context.Context, u string, timeout time.Duration) (eth2wrap.Client, error) {
bnHTTPClient, err := eth2http.New(ctx,
eth2http.WithAddress(u),
eth2http.WithTimeout(timeout),
eth2http.WithLogLevel(1), // zerolog.InfoLevel
)
if err != nil {
Expand All @@ -124,7 +131,7 @@ func eth2Client(ctx context.Context, u string) (eth2wrap.Client, error) {

bnClient := bnHTTPClient.(*eth2http.Service)

return eth2wrap.AdaptEth2HTTP(bnClient, 10*time.Second), nil
return eth2wrap.AdaptEth2HTTP(bnClient, timeout), nil
}

// signExit signs a voluntary exit message for valIdx with the given keyShare.
Expand Down
3 changes: 2 additions & 1 deletion cmd/exit_broadcast.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func newBcastFullExitCmd(runFunc func(context.Context, exitConfig) error) *cobra
{validatorPubkey, true},
{beaconNodeURL, true},
{exitFromFile, false},
{beaconNodeTimeout, false},
})

bindLogFlags(cmd.Flags(), &config.Log)
Expand All @@ -79,7 +80,7 @@ func runBcastFullExit(ctx context.Context, config exitConfig) error {

ctx = log.WithCtx(ctx, z.Str("validator", validator.String()))

eth2Cl, err := eth2Client(ctx, config.BeaconNodeURL)
eth2Cl, err := eth2Client(ctx, config.BeaconNodeURL, config.BeaconNodeTimeout)
if err != nil {
return errors.Wrap(err, "cannot create eth2 client for specified beacon node")
}
Expand Down
46 changes: 25 additions & 21 deletions cmd/exit_broadcast_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os"
"path/filepath"
"testing"
"time"

eth2v1 "github.com/attestantio/go-eth2-client/api/v1"
eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0"
Expand Down Expand Up @@ -109,13 +110,14 @@ func testRunBcastFullExitCmdFlow(t *testing.T, fromFile bool) {
baseDir := filepath.Join(root, fmt.Sprintf("op%d", idx))

config := exitConfig{
BeaconNodeURL: beaconMock.Address(),
ValidatorPubkey: lock.Validators[0].PublicKeyHex(),
PrivateKeyPath: filepath.Join(baseDir, "charon-enr-private-key"),
ValidatorKeysDir: filepath.Join(baseDir, "validator_keys"),
LockFilePath: filepath.Join(baseDir, "cluster-lock.json"),
PublishAddress: srv.URL,
ExitEpoch: 194048,
BeaconNodeURL: beaconMock.Address(),
ValidatorPubkey: lock.Validators[0].PublicKeyHex(),
PrivateKeyPath: filepath.Join(baseDir, "charon-enr-private-key"),
ValidatorKeysDir: filepath.Join(baseDir, "validator_keys"),
LockFilePath: filepath.Join(baseDir, "cluster-lock.json"),
PublishAddress: srv.URL,
ExitEpoch: 194048,
BeaconNodeTimeout: 30 * time.Second,
}

require.NoError(t, runSignPartialExit(ctx, config), "operator index: %v", idx)
Expand All @@ -124,13 +126,14 @@ func testRunBcastFullExitCmdFlow(t *testing.T, fromFile bool) {
baseDir := filepath.Join(root, fmt.Sprintf("op%d", 0))

config := exitConfig{
BeaconNodeURL: beaconMock.Address(),
ValidatorPubkey: lock.Validators[0].PublicKeyHex(),
PrivateKeyPath: filepath.Join(baseDir, "charon-enr-private-key"),
ValidatorKeysDir: filepath.Join(baseDir, "validator_keys"),
LockFilePath: filepath.Join(baseDir, "cluster-lock.json"),
PublishAddress: srv.URL,
ExitEpoch: 194048,
BeaconNodeURL: beaconMock.Address(),
ValidatorPubkey: lock.Validators[0].PublicKeyHex(),
PrivateKeyPath: filepath.Join(baseDir, "charon-enr-private-key"),
ValidatorKeysDir: filepath.Join(baseDir, "validator_keys"),
LockFilePath: filepath.Join(baseDir, "cluster-lock.json"),
PublishAddress: srv.URL,
ExitEpoch: 194048,
BeaconNodeTimeout: 30 * time.Second,
}

if fromFile {
Expand Down Expand Up @@ -273,13 +276,14 @@ func Test_runBcastFullExitCmd_Config(t *testing.T) {
baseDir := filepath.Join(root, "op0") // one operator is enough

config := exitConfig{
BeaconNodeURL: bnURL,
ValidatorPubkey: valAddr,
PrivateKeyPath: filepath.Join(baseDir, "charon-enr-private-key"),
ValidatorKeysDir: filepath.Join(baseDir, "validator_keys"),
LockFilePath: filepath.Join(baseDir, "cluster-lock.json"),
PublishAddress: oapiURL,
ExitEpoch: 0,
BeaconNodeURL: bnURL,
ValidatorPubkey: valAddr,
PrivateKeyPath: filepath.Join(baseDir, "charon-enr-private-key"),
ValidatorKeysDir: filepath.Join(baseDir, "validator_keys"),
LockFilePath: filepath.Join(baseDir, "cluster-lock.json"),
PublishAddress: oapiURL,
ExitEpoch: 0,
BeaconNodeTimeout: 30 * time.Second,
}

if test.badExistingExitPath {
Expand Down
16 changes: 9 additions & 7 deletions cmd/exit_fetch_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os"
"path/filepath"
"testing"
"time"

eth2v1 "github.com/attestantio/go-eth2-client/api/v1"
eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0"
Expand Down Expand Up @@ -93,13 +94,14 @@ func Test_runFetchExitFullFlow(t *testing.T) {
baseDir := filepath.Join(root, fmt.Sprintf("op%d", idx))

config := exitConfig{
BeaconNodeURL: beaconMock.Address(),
ValidatorPubkey: lock.Validators[0].PublicKeyHex(),
PrivateKeyPath: filepath.Join(baseDir, "charon-enr-private-key"),
ValidatorKeysDir: filepath.Join(baseDir, "validator_keys"),
LockFilePath: filepath.Join(baseDir, "cluster-lock.json"),
PublishAddress: srv.URL,
ExitEpoch: 194048,
BeaconNodeURL: beaconMock.Address(),
ValidatorPubkey: lock.Validators[0].PublicKeyHex(),
PrivateKeyPath: filepath.Join(baseDir, "charon-enr-private-key"),
ValidatorKeysDir: filepath.Join(baseDir, "validator_keys"),
LockFilePath: filepath.Join(baseDir, "cluster-lock.json"),
PublishAddress: srv.URL,
ExitEpoch: 194048,
BeaconNodeTimeout: 30 * time.Second,
}

require.NoError(t, runSignPartialExit(ctx, config), "operator index: %v", idx)
Expand Down
3 changes: 2 additions & 1 deletion cmd/exit_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func newListActiveValidatorsCmd(runFunc func(context.Context, exitConfig) error)
bindExitFlags(cmd, &config, []exitCLIFlag{
{lockFilePath, false},
{beaconNodeURL, true},
{beaconNodeTimeout, false},
})

bindLogFlags(cmd.Flags(), &config.Log)
Expand Down Expand Up @@ -74,7 +75,7 @@ func listActiveVals(ctx context.Context, config exitConfig) ([]string, error) {
return nil, errors.Wrap(err, "could not load cluster-lock.json")
}

eth2Cl, err := eth2Client(ctx, config.BeaconNodeURL)
eth2Cl, err := eth2Client(ctx, config.BeaconNodeURL, config.BeaconNodeTimeout)
if err != nil {
return nil, errors.Wrap(err, "cannot create eth2 client for specified beacon node")
}
Expand Down
34 changes: 19 additions & 15 deletions cmd/exit_list_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"math/rand"
"path/filepath"
"testing"
"time"

eth2v1 "github.com/attestantio/go-eth2-client/api/v1"
eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0"
Expand Down Expand Up @@ -75,11 +76,12 @@ func Test_runListActiveVals(t *testing.T) {
baseDir := filepath.Join(root, fmt.Sprintf("op%d", 0))

config := exitConfig{
BeaconNodeURL: beaconMock.Address(),
PrivateKeyPath: filepath.Join(baseDir, "charon-enr-private-key"),
ValidatorKeysDir: filepath.Join(baseDir, "validator_keys"),
LockFilePath: filepath.Join(baseDir, "cluster-lock.json"),
PlaintextOutput: true,
BeaconNodeURL: beaconMock.Address(),
PrivateKeyPath: filepath.Join(baseDir, "charon-enr-private-key"),
ValidatorKeysDir: filepath.Join(baseDir, "validator_keys"),
LockFilePath: filepath.Join(baseDir, "cluster-lock.json"),
PlaintextOutput: true,
BeaconNodeTimeout: 30 * time.Second,
}

require.NoError(t, runListActiveValidatorsCmd(ctx, config))
Expand Down Expand Up @@ -141,11 +143,12 @@ func Test_listActiveVals(t *testing.T) {
baseDir := filepath.Join(root, fmt.Sprintf("op%d", 0))

config := exitConfig{
BeaconNodeURL: beaconMock.Address(),
PrivateKeyPath: filepath.Join(baseDir, "charon-enr-private-key"),
ValidatorKeysDir: filepath.Join(baseDir, "validator_keys"),
LockFilePath: filepath.Join(baseDir, "cluster-lock.json"),
PlaintextOutput: true,
BeaconNodeURL: beaconMock.Address(),
PrivateKeyPath: filepath.Join(baseDir, "charon-enr-private-key"),
ValidatorKeysDir: filepath.Join(baseDir, "validator_keys"),
LockFilePath: filepath.Join(baseDir, "cluster-lock.json"),
PlaintextOutput: true,
BeaconNodeTimeout: 30 * time.Second,
}

vals, err := listActiveVals(ctx, config)
Expand Down Expand Up @@ -181,11 +184,12 @@ func Test_listActiveVals(t *testing.T) {
baseDir := filepath.Join(root, fmt.Sprintf("op%d", 0))

config := exitConfig{
BeaconNodeURL: beaconMock.Address(),
PrivateKeyPath: filepath.Join(baseDir, "charon-enr-private-key"),
ValidatorKeysDir: filepath.Join(baseDir, "validator_keys"),
LockFilePath: filepath.Join(baseDir, "cluster-lock.json"),
PlaintextOutput: true,
BeaconNodeURL: beaconMock.Address(),
PrivateKeyPath: filepath.Join(baseDir, "charon-enr-private-key"),
ValidatorKeysDir: filepath.Join(baseDir, "validator_keys"),
LockFilePath: filepath.Join(baseDir, "cluster-lock.json"),
PlaintextOutput: true,
BeaconNodeTimeout: 30 * time.Second,
}

vals, err := listActiveVals(ctx, config)
Expand Down
3 changes: 2 additions & 1 deletion cmd/exit_sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ func newSubmitPartialExitCmd(runFunc func(context.Context, exitConfig) error) *c
{exitEpoch, false},
{validatorPubkey, true},
{beaconNodeURL, true},
{beaconNodeTimeout, false},
})

bindLogFlags(cmd.Flags(), &config.Log)
Expand Down Expand Up @@ -99,7 +100,7 @@ func runSignPartialExit(ctx context.Context, config exitConfig) error {
return errors.New("validator not present in cluster lock", z.Str("validator", validator.String()))
}

eth2Cl, err := eth2Client(ctx, config.BeaconNodeURL)
eth2Cl, err := eth2Client(ctx, config.BeaconNodeURL, config.BeaconNodeTimeout)
if err != nil {
return errors.Wrap(err, "cannot create eth2 client for specified beacon node")
}
Expand Down
31 changes: 17 additions & 14 deletions cmd/exit_sign_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os"
"path/filepath"
"testing"
"time"

eth2v1 "github.com/attestantio/go-eth2-client/api/v1"
eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0"
Expand Down Expand Up @@ -120,13 +121,14 @@ func Test_runSubmitPartialExitFlow(t *testing.T) {
baseDir := filepath.Join(root, fmt.Sprintf("op%d", 0))

config := exitConfig{
BeaconNodeURL: beaconMock.Address(),
ValidatorPubkey: lock.Validators[0].PublicKeyHex(),
PrivateKeyPath: filepath.Join(baseDir, "charon-enr-private-key"),
ValidatorKeysDir: filepath.Join(baseDir, "validator_keys"),
LockFilePath: filepath.Join(baseDir, "cluster-lock.json"),
PublishAddress: srv.URL,
ExitEpoch: 194048,
BeaconNodeURL: beaconMock.Address(),
ValidatorPubkey: lock.Validators[0].PublicKeyHex(),
PrivateKeyPath: filepath.Join(baseDir, "charon-enr-private-key"),
ValidatorKeysDir: filepath.Join(baseDir, "validator_keys"),
LockFilePath: filepath.Join(baseDir, "cluster-lock.json"),
PublishAddress: srv.URL,
ExitEpoch: 194048,
BeaconNodeTimeout: 30 * time.Second,
}

require.NoError(t, runSignPartialExit(ctx, config))
Expand Down Expand Up @@ -258,13 +260,14 @@ func Test_runSubmitPartialExit_Config(t *testing.T) {
baseDir := filepath.Join(root, fmt.Sprintf("op%d", 0))

config := exitConfig{
BeaconNodeURL: bnURL,
ValidatorPubkey: valAddr,
PrivateKeyPath: filepath.Join(baseDir, "charon-enr-private-key"),
ValidatorKeysDir: filepath.Join(baseDir, "validator_keys"),
LockFilePath: filepath.Join(baseDir, "cluster-lock.json"),
PublishAddress: oapiURL,
ExitEpoch: 0,
BeaconNodeURL: bnURL,
ValidatorPubkey: valAddr,
PrivateKeyPath: filepath.Join(baseDir, "charon-enr-private-key"),
ValidatorKeysDir: filepath.Join(baseDir, "validator_keys"),
LockFilePath: filepath.Join(baseDir, "cluster-lock.json"),
PublishAddress: oapiURL,
ExitEpoch: 0,
BeaconNodeTimeout: 30 * time.Second,
}

require.ErrorContains(t, runSignPartialExit(ctx, config), test.errData)
Expand Down

0 comments on commit fff580a

Please sign in to comment.