Skip to content
Open
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
5 changes: 5 additions & 0 deletions beaconclient/prod_beacon_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,11 @@ type GetSpecResponse struct {
DomainAggregateAndProof string `json:"DOMAIN_AGGREGATE_AND_PROOF"` //nolint:tagliatelle
InactivityPenaltyQuotient string `json:"INACTIVITY_PENALTY_QUOTIENT"` //nolint:tagliatelle
InactivityPenaltyQuotientAltair string `json:"INACTIVITY_PENALTY_QUOTIENT_ALTAIR"` //nolint:tagliatelle
BellatrixForkVersion string `json:"BELLATRIX_FORK_VERSION"` //nolint:tagliatelle
CapellaForkVersion string `json:"CAPELLA_FORK_VERSION"` //nolint:tagliatelle
DenebForkVersion string `json:"DENEB_FORK_VERSION"` //nolint:tagliatelle
ElectraForkVersion string `json:"ELECTRA_FORK_VERSION"` //nolint:tagliatelle
FuluForkVersion string `json:"FULU_FORK_VERSION"` //nolint:tagliatelle
}

// GetSpec - https://ethereum.github.io/beacon-APIs/#/Config/getSpec
Expand Down
52 changes: 45 additions & 7 deletions cmd/api.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cmd

import (
"fmt"
"net/url"
"os"
"os/signal"
Expand Down Expand Up @@ -90,13 +91,6 @@ var apiCmd = &cobra.Command{
}
log.Infof("boost-relay %s", Version)

networkInfo, err := common.NewEthNetworkDetails(network)
if err != nil {
log.WithError(err).Fatalf("error getting network details")
}
log.Infof("Using network: %s", networkInfo.Name)
log.Debug(networkInfo.String())

// Connect to beacon clients and ensure it's synced
if len(beaconNodeURIs) == 0 {
log.Fatalf("no beacon endpoints specified")
Expand All @@ -117,6 +111,19 @@ var apiCmd = &cobra.Command{
}
beaconClient := beaconclient.NewMultiBeaconClient(log, beaconInstances)

if network == "" {
if err := loadSpecFromBeaconClient(beaconClient); err != nil {
log.WithError(err).Fatalf("error loading spec from beacon client")
}
}

networkInfo, err := common.NewEthNetworkDetails(network)
if err != nil {
log.WithError(err).Fatalf("error getting network details")
}
Comment on lines +114 to +123

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description says specs should be loaded when the network is not part of presets, but the current logic only loads from the beacon when network == \"\". If a user sets --network custom, they still won’t get auto-loading. Consider triggering loadSpecFromBeaconClient for network == common.EthNetworkCustom (or introducing an explicit --network auto/beacon value) and then passing a non-empty value into NewEthNetworkDetails so logs/config reflect the mode.

Copilot uses AI. Check for mistakes.
log.Infof("Using network: %s", networkInfo.Name)
log.Debug(networkInfo.String())

// Connect to Redis
if redisReadonlyURI == "" {
log.Infof("Connecting to Redis at %s ...", redisURI)
Expand Down Expand Up @@ -217,3 +224,34 @@ var apiCmd = &cobra.Command{
log.Info("bye")
},
}

// loadSpecFromBeaconClient fetches the chain spec and genesis data from the beacon client
// and sets the corresponding environment variables for fork versions and genesis info.
// This allows the relay to automatically configure network parameters without manual specification.
func loadSpecFromBeaconClient(beaconClient *beaconclient.MultiBeaconClient) error {
spec, err := beaconClient.GetSpec()
if err != nil {
return err
}

genesis, err := beaconClient.GetGenesis()
if err != nil {
return err
Comment on lines +234 to +239

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These errors are returned without context, which makes failures harder to diagnose in logs (spec vs genesis vs transport). Wrap them with operation context (e.g., "get beacon spec" / "get beacon genesis") so callers get actionable messages.

Suggested change
return err
}
genesis, err := beaconClient.GetGenesis()
if err != nil {
return err
return fmt.Errorf("get beacon spec: %w", err)
}
genesis, err := beaconClient.GetGenesis()
if err != nil {
return fmt.Errorf("get beacon genesis: %w", err)

Copilot uses AI. Check for mistakes.
}

envs := map[string]string{
"GENESIS_FORK_VERSION": genesis.Data.GenesisForkVersion,
"GENESIS_VALIDATORS_ROOT": genesis.Data.GenesisValidatorsRoot,
"BELLATRIX_FORK_VERSION": spec.BellatrixForkVersion,
"CAPELLA_FORK_VERSION": spec.CapellaForkVersion,
"DENEB_FORK_VERSION": spec.DenebForkVersion,
"ELECTRA_FORK_VERSION": spec.ElectraForkVersion,
"FULU_FORK_VERSION": spec.FuluForkVersion,
}
for k, v := range envs {
if err := os.Setenv(k, v); err != nil {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still using env variables such that I do not have to modify NewEthNetworkDetails.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this would be great to leave as comment

return fmt.Errorf("failed to set env var %s: %w", k, err)
}
}
Comment on lines +242 to +255

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This unconditionally overwrites any user-provided env vars. If someone set fork versions explicitly (intentionally overriding the beacon), this will silently replace them. Consider only setting values when the env var is not already present (using os.LookupEnv), or clearly defining/implementing precedence (e.g., env overrides beacon, or vice versa) with logging.

Copilot uses AI. Check for mistakes.
return nil
}
3 changes: 3 additions & 0 deletions common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ var (
EthNetworkHoodi = "hoodi"
EthNetworkMainnet = "mainnet"
EthNetworkCustom = "custom"
EthNetworkBeacon = ""

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using an empty string as a named network constant is ambiguous and makes it hard to distinguish between “unset flag” and an intentional mode. It also results in EthNetworkDetails.Name being empty for this path. Prefer a non-empty explicit value (e.g., \"beacon\"/\"auto\") or keep empty as invalid and handle the auto-load decision entirely in cmd/api.go (e.g., set network = common.EthNetworkCustom after loading env vars).

Suggested change
EthNetworkBeacon = ""
EthNetworkBeacon = "beacon"

Copilot uses AI. Check for mistakes.

GenesisForkVersionHolesky = "0x01017000"
GenesisForkVersionSepolia = "0x90000069"
Expand Down Expand Up @@ -155,6 +156,8 @@ func NewEthNetworkDetails(networkName string) (ret *EthNetworkDetails, err error
denebForkVersion = DenebForkVersionMainnet
electraForkVersion = ElectraForkVersionMainnet
fuluForkVersion = FuluForkVersionMainnet
case EthNetworkBeacon:
fallthrough
Comment on lines +159 to +160

Copilot AI Feb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using an empty string as a named network constant is ambiguous and makes it hard to distinguish between “unset flag” and an intentional mode. It also results in EthNetworkDetails.Name being empty for this path. Prefer a non-empty explicit value (e.g., \"beacon\"/\"auto\") or keep empty as invalid and handle the auto-load decision entirely in cmd/api.go (e.g., set network = common.EthNetworkCustom after loading env vars).

Suggested change
case EthNetworkBeacon:
fallthrough

Copilot uses AI. Check for mistakes.
case EthNetworkCustom:
genesisForkVersion = os.Getenv("GENESIS_FORK_VERSION")
genesisValidatorsRoot = os.Getenv("GENESIS_VALIDATORS_ROOT")
Expand Down