Skip to content

rpc: public testnet endpoints disagree on standard methods, and identical eth_getProof requests return two different error codes #371

Description

@osr21

Summary

The four public testnet RPC endpoints referenced in docs/running-an-arc-node.md do not serve the same JSON-RPC method set, and two of them return two different, semantically contradictory error codes for byte-identical requests. A client written against the canonical rpc.testnet.arc.io cannot use EIP-1186 state proofs at all, while the same call succeeds on rpc.drpc.testnet.arc.io.

Capability matrix

Single (non-batch) requests, 3 consecutive samples each:

Endpoint eth_getProof eth_createAccessList
rpc.testnet.arc.io -32014 / -32601 (varies) -32601 ×3
rpc.drpc.testnet.arc.io OK ×3 OK ×3
rpc.quicknode.testnet.arc.io -32601 / -32014 (varies) -32601 ×3
rpc.blockdaemon.testnet.arc.io OK ×3 -32003 "Request method filtered" ×3

Also unavailable on the canonical endpoint: eth_simulateV1, eth_blobBaseFee, txpool_status (all -32601). eth_getBlockReceipts and eth_maxPriorityFeePerGas work.

The part that actually breaks clients: unstable error codes

Ten consecutive identical eth_getProof calls, same hostname, ~0.8s apart:

rpc.testnet.arc.io            -32014 -32601 -32601 -32014 -32014 -32014 -32014 -32014 -32014 -32014
rpc.quicknode.testnet.arc.io  -32601 -32601 -32601 -32601 -32601 -32014 -32014 -32601 -32014 -32014

So rpc.testnet.arc.io answered -32014 8/10 and -32601 2/10; quicknode split 6/4 the other way. These two codes mean opposite things to a client:

  • -32601 "method not supported" is permanent — a well-behaved client disables the code path or falls back.
  • -32014 "requested data not available" reads as transient/state-related — a client retries, or retries against a different block.

Whichever branch a library takes, it takes at random, per call. Retry logic and capability detection both become nondeterministic. Any capability probe run once at startup will cache whichever answer it happened to get.

Batch semantics diverge too

Batch of three, where the middle request is eth_createAccessList:

rpc.testnet.arc.io             array of 3 -> id=1:OK, id=2:-32601, id=3:OK
rpc.drpc.testnet.arc.io        array of 3 -> id=1:OK, id=2:OK,     id=3:OK
rpc.quicknode.testnet.arc.io   array of 3 -> id=1:OK, id=2:-32601, id=3:OK
rpc.blockdaemon.testnet.arc.io TOP-LEVEL ERROR (whole batch rejected): {"code":-32003,"message":"Request method filtered"}

Blockdaemon discards the entire batch and replies with a single error object ("id": null) instead of an array. JSON-RPC 2.0 §6 specifies a batch response as an array of individual responses, so a single filtered method silently kills the two unrelated calls beside it. Confirmed it is the method and not batching in general — a batch of eth_chainId + eth_blockNumber succeeds on that endpoint, and eth_getProof on its own succeeds there too.

Where this appears to come from

grep -rIn 'getProof\|get_proof\|create_access_list' --include='*.rs' crates/ returns nothing, so these are inherited from upstream reth rather than overridden in this repo, and rpc.drpc.testnet.arc.io returns real proofs for the same address and block tag. The state is therefore available on the network; the divergence looks like per-provider gateway policy rather than a node capability limit. That also explains the intra-endpoint variation, which is consistent with load-balancing across a fleet whose members do not answer identically.

This matters for the docs specifically because docs/running-an-arc-node.md lists these endpoints together as interchangeable --follow.endpoint values (lines ~211-213 and ~883-885), which reads as "equivalent".

Suggested resolution

Any one of these would remove the ambiguity:

  1. Serve a consistent method set across the documented endpoints, or
  2. Document per-endpoint capabilities so eth_getProof consumers know to select drpc, and
  3. Return a stable code for an unsupported method on a given endpoint — pick -32601 and keep it, so capability detection and retry logic are deterministic.

The batch behaviour on blockdaemon is worth raising with that provider separately; a filtered method should produce a per-request error inside the array, not a top-level rejection.

Reproduction

curl -s -X POST -H 'Content-Type: application/json' \
  --data '{"jsonrpc":"2.0","id":1,"method":"eth_getProof","params":["0x3600000000000000000000000000000000000000",["0x0"],"latest"]}' \
  https://rpc.testnet.arc.io

Repeat ~10 times against each host and compare. Chain id 0x4cef52 (5042002) on all four.


Disclosure: I am external to Circle — an unaffiliated community contributor with no write access to this repository. This report is advisory only; measurements are from my own probing of the public endpoints and should be reproduced before acting on them.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions