diff --git a/node/core/executor.go b/node/core/executor.go index 0f16ca00..52d30125 100644 --- a/node/core/executor.go +++ b/node/core/executor.go @@ -105,8 +105,8 @@ func NewExecutor(newSyncFunc NewSyncerFunc, config *Config, tmPubKey crypto.PubK logger.Info("L2Next geth not configured (no upgrade switch)") } - // Fetch geth config at startup - gethCfg, err := types.FetchGethConfig(config.L2.EthAddr, logger) + // Fetch geth config at startup (with retry to wait for geth) + gethCfg, err := types.FetchGethConfigWithRetry(config.L2.EthAddr, logger) if err != nil { return nil, fmt.Errorf("failed to fetch geth config: %w", err) } diff --git a/node/derivation/derivation.go b/node/derivation/derivation.go index e5c1a539..a5b013f7 100644 --- a/node/derivation/derivation.go +++ b/node/derivation/derivation.go @@ -139,8 +139,8 @@ func NewDerivationClient(ctx context.Context, cfg *Config, syncer *sync.Syncer, baseHttp := NewBasicHTTPClient(cfg.BeaconRpc, logger) l1BeaconClient := NewL1BeaconClient(baseHttp) - // Fetch geth config once at startup for root validation skip logic - gethCfg, err := types.FetchGethConfig(cfg.L2.EthAddr, logger) + // Fetch geth config once at startup for root validation skip logic (with retry) + gethCfg, err := types.FetchGethConfigWithRetry(cfg.L2.EthAddr, logger) if err != nil { return nil, fmt.Errorf("failed to fetch geth config: %w", err) } diff --git a/node/types/retryable_client.go b/node/types/retryable_client.go index dfdfcf80..4f29e31f 100644 --- a/node/types/retryable_client.go +++ b/node/types/retryable_client.go @@ -29,6 +29,10 @@ const ( ExecutionAborted = "execution aborted" Timeout = "timed out" DiscontinuousBlockError = "discontinuous block number" + + // Geth connection retry settings + GethRetryAttempts = 60 // max retry attempts + GethRetryInterval = 5 * time.Second // interval between retries ) // configResponse represents the eth_config RPC response (EIP-7910) @@ -60,6 +64,21 @@ type GethConfig struct { UseZktrie bool } +// FetchGethConfigWithRetry fetches geth config with retry, waiting for geth to be ready. +func FetchGethConfigWithRetry(rpcURL string, logger tmlog.Logger) (*GethConfig, error) { + var lastErr error + for i := 0; i < GethRetryAttempts; i++ { + config, err := FetchGethConfig(rpcURL, logger) + if err == nil { + return config, nil + } + lastErr = err + logger.Info("Waiting for geth to be ready...", "attempt", i+1, "error", err) + time.Sleep(GethRetryInterval) + } + return nil, fmt.Errorf("geth not ready after %d attempts: %w", GethRetryAttempts, lastErr) +} + // FetchGethConfig fetches the geth configuration via eth_config API func FetchGethConfig(rpcURL string, logger tmlog.Logger) (*GethConfig, error) { client, err := rpc.Dial(rpcURL) @@ -128,7 +147,6 @@ func (rc *RetryableClient) MPTForkTime() uint64 { return rc.switchTime } - // NewRetryableClient creates a new retryable client with the given switch time. // Will retry calling the api, if the connection is refused. // @@ -152,7 +170,6 @@ func NewRetryableClient(authClient *authclient.Client, ethClient *ethclient.Clie logger: logger, } } - // Check if switch time has already passed at startup now := uint64(time.Now().Unix()) alreadySwitched := switchTime > 0 && now >= switchTime