Skip to content

Conversation

@klim0v
Copy link
Contributor

@klim0v klim0v commented May 24, 2025

Add ValidateMetadata hook to CommonOpts

Summary

This PR introduces a new, optional ValidateMetadata hook to the CommonOpts struct in go-eth2-client. This function receives a map[string]any containing the metadata returned by a beacon node (for example, the dependent_root in validator duties responses) and returns an error if the metadata should be considered invalid. If provided, the client library will invoke this hook on all API calls that return metadata before accepting the result. This allows applications to reject stale or inconsistent data from individual nodes before it is used.

Motivation

In multi-node beacon chain setups (e.g. using a pool of beacon clients for high availability), a synchronization issue can occur: one node may respond faster but with outdated state, leading to incorrect metadata. For example, after subscribing to head events, a call to GET /eth/v1/validator/duties/proposer/{epoch} could return results from a client that isn’t yet fully synced. Its dependent_root (the block root that the response depends on) might be behind the latest head, resulting in inconsistent or stale duties being returned.

The ValidateMetadata hook addresses this by giving users a way to programmatically inspect and validate the returned metadata before it is accepted. If the hook returns an error, the library treats that response as invalid, allowing the caller to retry the request against another client. This mechanism is particularly useful for ensuring that all clients in a multi-instance configuration return coherent and up-to-date metadata.

Implementation

  • A new field ValidateMetadata func(map[string]any) error is added to the CommonOpts struct. It is optional; if it is nil, no validation is performed (preserving the existing behavior).

  • All relevant API handlers that process metadata now invoke this hook. For example, in the proposer duties provider (and similarly in attester duties, sync committee duties, etc.), after unmarshalling the response, the code does:

    if opts.Common.ValidateMetadata != nil {
        if err := opts.Common.ValidateMetadata(metadata); err != nil {
            return nil, err
        }
    }

    This ensures that we only return a result if the metadata passes validation.

  • No existing endpoints or return types are changed. The hook is simply an additional layer on top of the response processing.

Benefits

  • Improved Consistency: Users can ensure critical metadata fields (such as dependent_root, execution_optimistic, etc.) meet their expectations. This avoids using stale or invalid metadata for validator duties or other operations.
  • General-Purpose Hook: The ValidateMetadata function can contain any custom logic. For instance, it could verify chain IDs, check block roots, validate timestamps, or enforce other application-specific rules on the metadata.
  • Enhanced Multi-Client Handling: In multi-node setups, this feature helps filter out undesirable responses (from lagging or misbehaving nodes) without modifying the library itself. Callers can automatically fall back to another client if validation fails.
  • Backward-Compatible: Because the hook is optional and disabled by default, existing users see no change in behavior unless they opt into it.

Backward Compatibility

  • The new ValidateMetadata field is optional and defaults to nil. Existing code that does not use this field will operate exactly as before.
  • No existing APIs, function signatures, or response formats are changed or removed.
  • Because this feature is opt-in, upgrading to include this change is safe for existing users.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant