Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd: beacon node API timeouts #3164

Merged
merged 4 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 38 additions & 22 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,6 @@
"github.com/obolnetwork/charon/testutil/beaconmock" // Allow testutil
)

const eth2ClientTimeout = time.Second * 2

type Config struct {
P2P p2p.Config
Log log.Config
Expand All @@ -80,6 +78,8 @@
DebugAddr string
ValidatorAPIAddr string
BeaconNodeAddrs []string
BeaconNodeTimeout time.Duration
BeaconNodeSubmitTimeout time.Duration
JaegerAddr string
JaegerService string
SimnetBMock bool
Expand Down Expand Up @@ -225,7 +225,7 @@

initStartupMetrics(p2p.PeerName(tcpNode.ID()), int(cluster.Threshold), len(cluster.Operators), len(cluster.Validators), network)

eth2Cl, err := newETH2Client(ctx, conf, life, cluster, cluster.ForkVersion)
eth2Cl, subEth2Cl, err := newETH2Client(ctx, conf, life, cluster, cluster.GetForkVersion(), conf.BeaconNodeTimeout, conf.BeaconNodeSubmitTimeout)

Check warning on line 228 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L228

Added line #L228 was not covered by tests
if err != nil {
return err
}
Expand Down Expand Up @@ -271,7 +271,7 @@
wireMonitoringAPI(ctx, life, conf.MonitoringAddr, conf.DebugAddr, tcpNode, eth2Cl, peerIDs,
promRegistry, qbftDebug, pubkeys, seenPubkeys, vapiCalls, len(cluster.GetValidators()))

err = wireCoreWorkflow(ctx, life, conf, cluster, nodeIdx, tcpNode, p2pKey, eth2Cl,
err = wireCoreWorkflow(ctx, life, conf, cluster, nodeIdx, tcpNode, p2pKey, eth2Cl, subEth2Cl,

Check warning on line 274 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L274

Added line #L274 was not covered by tests
peerIDs, sender, qbftDebug.AddInstance, seenPubkeysFunc, vapiCallsFunc)
if err != nil {
return err
Expand Down Expand Up @@ -343,7 +343,7 @@
// wireCoreWorkflow wires the core workflow components.
func wireCoreWorkflow(ctx context.Context, life *lifecycle.Manager, conf Config,
cluster *manifestpb.Cluster, nodeIdx cluster.NodeIdx, tcpNode host.Host, p2pKey *k1.PrivateKey,
eth2Cl eth2wrap.Client, peerIDs []peer.ID, sender *p2p.Sender,
eth2Cl, submissionEth2Cl eth2wrap.Client, peerIDs []peer.ID, sender *p2p.Sender,
qbftSniffer func(*pbv1.SniffedConsensusInstance), seenPubkeys func(core.PubKey),
vapiCalls func(),
) error {
Expand Down Expand Up @@ -509,7 +509,7 @@
aggSigDB = aggsigdb.NewMemDB(deadlinerFunc("aggsigdb"))
}

broadcaster, err := bcast.New(ctx, eth2Cl)
broadcaster, err := bcast.New(ctx, submissionEth2Cl)

Check warning on line 512 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L512

Added line #L512 was not covered by tests
if err != nil {
return err
}
Expand Down Expand Up @@ -776,14 +776,12 @@
return pubkeys, nil
}

// newETH2Client returns a new eth2client; it is either a beaconmock for
// newETH2Client returns a new eth2client for the configured timeouts; it is either a beaconmock for
// simnet or a multi http client to a real beacon node.
func newETH2Client(ctx context.Context, conf Config, life *lifecycle.Manager,
cluster *manifestpb.Cluster, forkVersion []byte,
) (eth2wrap.Client, error) {
func newETH2Client(ctx context.Context, conf Config, life *lifecycle.Manager, cluster *manifestpb.Cluster, forkVersion []byte, bnTimeout time.Duration, submissionBnTimeout time.Duration) (eth2wrap.Client, eth2wrap.Client, error) {

Check warning on line 781 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L781

Added line #L781 was not covered by tests
pubkeys, err := eth2PubKeys(cluster)
if err != nil {
return nil, err
return nil, nil, err

Check warning on line 784 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L784

Added line #L784 was not covered by tests
}

// Default to 1s slot duration if not set.
Expand All @@ -795,23 +793,23 @@
log.Info(ctx, "Beaconmock fuzz configured!")
bmock, err := beaconmock.New(beaconmock.WithBeaconMockFuzzer(), beaconmock.WithForkVersion([4]byte(forkVersion)))
if err != nil {
return nil, err
return nil, nil, err

Check warning on line 796 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L796

Added line #L796 was not covered by tests
}

wrap, err := eth2wrap.Instrument(bmock)
if err != nil {
return nil, err
return nil, nil, err

Check warning on line 801 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L801

Added line #L801 was not covered by tests
}

life.RegisterStop(lifecycle.StopBeaconMock, lifecycle.HookFuncErr(bmock.Close))

return wrap, nil
return wrap, nil, nil

Check warning on line 806 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L806

Added line #L806 was not covered by tests
}

if conf.SimnetBMock { // Configure the beacon mock.
genesisTime, err := eth2util.ForkVersionToGenesisTime(forkVersion)
if err != nil {
return nil, err
return nil, nil, err

Check warning on line 812 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L812

Added line #L812 was not covered by tests
}

const dutyFactor = 100 // Duty factor spreads duties deterministically in an epoch.
Expand All @@ -828,12 +826,12 @@
opts = append(opts, conf.TestConfig.SimnetBMockOpts...)
bmock, err := beaconmock.New(opts...)
if err != nil {
return nil, err
return nil, nil, err

Check warning on line 829 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L829

Added line #L829 was not covered by tests
}

wrap, err := eth2wrap.Instrument(bmock)
if err != nil {
return nil, err
return nil, nil, err

Check warning on line 834 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L834

Added line #L834 was not covered by tests
}

if conf.SyntheticBlockProposals {
Expand All @@ -843,20 +841,38 @@

life.RegisterStop(lifecycle.StopBeaconMock, lifecycle.HookFuncErr(bmock.Close))

return wrap, nil
return wrap, wrap, nil

Check warning on line 844 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L844

Added line #L844 was not covered by tests
}

if len(conf.BeaconNodeAddrs) == 0 {
return nil, errors.New("beacon node endpoints empty")
return nil, nil, errors.New("beacon node endpoints empty")
}

Check warning on line 849 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L848-L849

Added lines #L848 - L849 were not covered by tests

if conf.SyntheticBlockProposals {
log.Info(ctx, "Synthetic block proposals enabled")
}

Check warning on line 853 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L851-L853

Added lines #L851 - L853 were not covered by tests

eth2Cl, err := configureEth2Client(ctx, forkVersion, conf.BeaconNodeAddrs, bnTimeout, conf.SyntheticBlockProposals)
if err != nil {
return nil, nil, errors.Wrap(err, "new eth2 http client")

Check warning on line 857 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L855-L857

Added lines #L855 - L857 were not covered by tests
}

eth2Cl, err := eth2wrap.NewMultiHTTP(eth2ClientTimeout, [4]byte(forkVersion), conf.BeaconNodeAddrs...)
submissionEth2Cl, err := configureEth2Client(ctx, forkVersion, conf.BeaconNodeAddrs, submissionBnTimeout, conf.SyntheticBlockProposals)
if err != nil {
return nil, nil, errors.Wrap(err, "new submission eth2 http client")
}

Check warning on line 863 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L860-L863

Added lines #L860 - L863 were not covered by tests

return eth2Cl, submissionEth2Cl, nil

Check warning on line 865 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L865

Added line #L865 was not covered by tests
}

// configureEth2Client configures a beacon node client with the provided settings.
func configureEth2Client(ctx context.Context, forkVersion []byte, addrs []string, timeout time.Duration, syntheticBlockProposals bool) (eth2wrap.Client, error) {
eth2Cl, err := eth2wrap.NewMultiHTTP(timeout, [4]byte(forkVersion), addrs...)

Check warning on line 870 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L869-L870

Added lines #L869 - L870 were not covered by tests
if err != nil {
return nil, errors.Wrap(err, "new eth2 http client")
}

if conf.SyntheticBlockProposals {
log.Info(ctx, "Synthetic block proposals enabled")
if syntheticBlockProposals {

Check warning on line 875 in app/app.go

View check run for this annotation

Codecov / codecov/patch

app/app.go#L875

Added line #L875 was not covered by tests
eth2Cl = eth2wrap.WithSyntheticDuties(eth2Cl)
}

Expand Down
48 changes: 26 additions & 22 deletions cmd/cmd_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,19 @@ func TestCmdFlags(t *testing.T) {
Enabled: nil,
Disabled: nil,
},
LockFile: ".charon/cluster-lock.json",
ManifestFile: ".charon/cluster-manifest.pb",
PrivKeyFile: ".charon/charon-enr-private-key",
PrivKeyLocking: false,
SimnetValidatorKeysDir: ".charon/validator_keys",
SimnetSlotDuration: time.Second,
MonitoringAddr: "127.0.0.1:3620",
ValidatorAPIAddr: "127.0.0.1:3600",
BeaconNodeAddrs: []string{"http://beacon.node"},
JaegerAddr: "",
JaegerService: "charon",
LockFile: ".charon/cluster-lock.json",
ManifestFile: ".charon/cluster-manifest.pb",
PrivKeyFile: ".charon/charon-enr-private-key",
PrivKeyLocking: false,
SimnetValidatorKeysDir: ".charon/validator_keys",
SimnetSlotDuration: time.Second,
MonitoringAddr: "127.0.0.1:3620",
ValidatorAPIAddr: "127.0.0.1:3600",
BeaconNodeAddrs: []string{"http://beacon.node"},
BeaconNodeTimeout: 2 * time.Second,
BeaconNodeSubmitTimeout: 2 * time.Second,
JaegerAddr: "",
JaegerService: "charon",
},
},
{
Expand Down Expand Up @@ -119,17 +121,19 @@ func TestCmdFlags(t *testing.T) {
Enabled: nil,
Disabled: nil,
},
LockFile: ".charon/cluster-lock.json",
ManifestFile: ".charon/cluster-manifest.pb",
PrivKeyFile: ".charon/charon-enr-private-key",
PrivKeyLocking: false,
SimnetValidatorKeysDir: ".charon/validator_keys",
SimnetSlotDuration: time.Second,
MonitoringAddr: "127.0.0.1:3620",
ValidatorAPIAddr: "127.0.0.1:3600",
BeaconNodeAddrs: []string{"http://beacon.node"},
JaegerAddr: "",
JaegerService: "charon",
LockFile: ".charon/cluster-lock.json",
ManifestFile: ".charon/cluster-manifest.pb",
PrivKeyFile: ".charon/charon-enr-private-key",
PrivKeyLocking: false,
SimnetValidatorKeysDir: ".charon/validator_keys",
SimnetSlotDuration: time.Second,
MonitoringAddr: "127.0.0.1:3620",
ValidatorAPIAddr: "127.0.0.1:3600",
BeaconNodeAddrs: []string{"http://beacon.node"},
BeaconNodeTimeout: 2 * time.Second,
BeaconNodeSubmitTimeout: 2 * time.Second,
JaegerAddr: "",
JaegerService: "charon",
TestConfig: app.TestConfig{
P2PFuzz: true,
},
Expand Down
5 changes: 5 additions & 0 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import (
"github.com/obolnetwork/charon/p2p"
)

// eth2ClientTimeout is the default timeout for charon <> beacon node API interactions.
const eth2ClientTimeout = time.Second * 2

func newRunCmd(runFunc func(context.Context, app.Config) error, unsafe bool) *cobra.Command {
var conf app.Config

Expand Down Expand Up @@ -71,6 +74,8 @@ func bindRunFlags(cmd *cobra.Command, config *app.Config) {
cmd.Flags().StringVar(&config.LockFile, "lock-file", ".charon/cluster-lock.json", "The path to the cluster lock file defining the distributed validator cluster. If both cluster manifest and cluster lock files are provided, the cluster manifest file takes precedence.")
cmd.Flags().StringVar(&config.ManifestFile, "manifest-file", ".charon/cluster-manifest.pb", "The path to the cluster manifest file. If both cluster manifest and cluster lock files are provided, the cluster manifest file takes precedence.")
cmd.Flags().StringSliceVar(&config.BeaconNodeAddrs, "beacon-node-endpoints", nil, "Comma separated list of one or more beacon node endpoint URLs.")
cmd.Flags().DurationVar(&config.BeaconNodeTimeout, "beacon-node-timeout", eth2ClientTimeout, "Timeout for the HTTP requests Charon makes to the configured beacon nodes.")
cmd.Flags().DurationVar(&config.BeaconNodeSubmitTimeout, "beacon-node-submit-timeout", eth2ClientTimeout, "Timeout for the submission-related HTTP requests Charon makes to the configured beacon nodes.")
cmd.Flags().StringVar(&config.ValidatorAPIAddr, "validator-api-address", "127.0.0.1:3600", "Listening address (ip and port) for validator-facing traffic proxying the beacon-node API.")
cmd.Flags().StringVar(&config.JaegerAddr, "jaeger-address", "", "Listening address for jaeger tracing.")
cmd.Flags().StringVar(&config.JaegerService, "jaeger-service", "charon", "Service name used for jaeger tracing.")
Expand Down
Loading
Loading