ObolNetwork / ObolNetwork/obol-stack

eRPC: add consensus failsafe (maxParticipants=3, agreementThreshold=2) for state-read methods

Open
#358 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
Go
Stars
11
Forks
1
PR merge metrics
No merged PRs in 30d

Description

Problem

The obol-stack eRPC config (internal/embed/infrastructure/values/erpc.yaml.gotmpl) routes each RPC request to a single upstream chosen by eRPC's selectionPolicy + score, with hedge for latency fallback. For four EVM networks (mainnet, hoodi, base, base-sepolia) we currently rely on one upstream's answer per request.

For our read paths that underpin payment/registration correctness — agent-registration document fetches, ERC-8004 registry reads, USDC balance checks, eth_call of payment requirements — a single malicious or desynced upstream can return a wrong answer that no layer above detects. Consensus validation between multiple upstreams catches this cheaply.

Proposed config

Add a consensus entry to the failsafe list for high-trust read methods on each EVM network:

failsafe:
  - matchMethod: "eth_call|eth_getLogs|eth_getTransactionReceipt|eth_getTransactionByHash|eth_getBlockByNumber|eth_getBlockByHash|eth_chainId"
    consensus:
      maxParticipants: 3        # fan out to 3 upstreams in parallel
      agreementThreshold: 2     # 2 of 3 must match → return majority answer
      punishMisbehavior:
        disputeThreshold: 3
        disputeWindow: 10s
        sitOutPenalty: 5m
  - matchMethod: "*"            # non-consensus path stays for latency-sensitive reads
    timeout:
      duration: 30s
    retry:
      maxAttempts: 2
      delay: 100ms
    hedge:
      delay: 500ms
      maxCount: 1

Apply to all four EVM network blocks (lines ~80-145 of the gotmpl). Keep the existing selectionPolicy intact for eth_sendRawTransaction routing; consensus only activates for read methods.

Why 3/2, not 2/2

  • 2/2 means every paid request fails as soon as one upstream is flaky → negates the resilience we already have.
  • 3/2 tolerates one upstream failure/disagreement per request, returns the majority answer, and the punishMisbehavior block auto-quarantines consistently-misbehaving upstreams for 5 min.

Upstream prerequisite

Each affected chain must have ≥ 3 upstreams configured in the upstreams: array for consensus to have anyone to vote with. Current state:

  • chainId: 1 (mainnet) — verify count; add more public RPCs via obol network add if needed.
  • chainId: 560048 (hoodi) — likely only 1 today.
  • chainId: 8453 (base) — verify.
  • chainId: 84532 (base-sepolia) — 1 (base-sepolia-publicnode) + whatever is added by obol network add.

When count < 3, eRPC degrades gracefully — it queries however many exist — but the resilience goal isn't met. So this issue should include bumping the default ChainList seed count or guaranteeing a minimum.

Explicit non-goals

  • Do not apply consensus to eth_sendRawTransaction — routing stays single-upstream (already handled by selectionPolicy).
  • Do not apply to eth_blockNumber/eth_syncing/latency-critical head checks — treat those as the matchMethod: "*" fallthrough.
  • Do not use agreementThreshold: 3 (of 3) — one slow upstream fails every call.

Optional: nonce handling

For eth_getTransactionCount, lagging replicas routinely disagree. Instead of strict consensus, use:

- matchMethod: "eth_getTransactionCount"
  consensus:
    maxParticipants: 3
    agreementThreshold: 1
    preferHighestValueFor:
      eth_getTransactionCount:
        - result

This returns the highest observed nonce, preventing stale-nonce transaction failures.

Validation plan

  • Unit: internal/network/erpc_test.go — template render with the new failsafe block.
  • Integration: seed 3 base-sepolia RPCs (publicnode + alchemy public + drpc public). Probe eth_call against the registry contract; flip one upstream to return wrong data (mock); confirm request still returns majority answer.
  • Observability: eRPC emits metrics on consensus participation — expose them in Grafana.

References

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with internal/embed/infrastructure/values/erpc.yaml.gotmpl and inspect the four EVM network blocks and their upstreams arrays. Then read internal/network/erpc_test.go and render the template to verify the failsafe configuration, upstream counts, and preservation of eth_sendRawTransaction routing. Done means the configuration and validation cover the requested consensus behavior and network prerequisites.

Written by the indexing model from the issue text.

Assessment

Tech stack
yaml
Domain
infrastructure
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.