RPC Endpoint Requirements
A ciphernode reads the full contract history from an Ethereum RPC endpoint before it can take part in a committee. Many setup problems come from the endpoint, not from the node. This page explains what the node asks from an endpoint, gives a one-command test, and explains the sync messages. At the end, you know whether an endpoint is usable before you start a node with it.
What the node needs
| Requirement | Value | Reason |
|---|---|---|
| Log history | From the lowest configured deploy_block | The first sync reads every contract event from that block. |
eth_getLogs range | 10,000 blocks, or a smaller cap | The node starts at 10,000 blocks (rpc_log_range_blocks) and halves the range when the provider refuses it. |
| WebSocket | A ws:// or wss:// URL | After the sync, an eth_subscribe subscription announces new logs. |
| Rate limits | Short bursts are acceptable | The node retries HTTP 429 and 503 responses (see below). |
| Historical state | Not needed | The node replays history from logs. It does not read contract state at old blocks. |
An endpoint can keep old logs and not keep old state, or the opposite. A test of historical state
(eth_getCode or eth_call at an old block) therefore tells you nothing about log history.
Use one provider for the subscription and for the requests of a chain. Do not put a round-robin pool of separately synced providers behind one URL. The subscription can announce a block before another member of the pool can return that block or its logs.
How the first sync works
- Start blockThe lowest deploy_block of all configured contracts.
- Fetch logseth_getLogs in ranges of 10,000 blocks.
- NarrowHalve the range when the provider refuses it.
- Subscribeeth_subscribe announces new logs. The node reads them with eth_getLogs.
The node reads logs from the start block to the chain head. Each eth_getLogs request covers 10,000
blocks, or the chain's rpc_log_range_blocks. When the provider refuses a range, the node halves
the range and sends the request again. The smallest range is one block. The node keeps the narrower
width for the rest of the sync.
The node recognizes a refusal that names a block range or a result-size limit, for example the
messages in the table under Measured endpoints. A provider that refuses a
range with other words makes the node retry and fail instead of narrowing. Set
rpc_log_range_blocks to the provider's documented cap for such a provider. The same key raises the
range for a provider that permits more than 10,000 blocks, which makes the first sync faster.
A narrow range makes the first sync slower. A history of 5,000,000 blocks takes 500 requests at 10,000 blocks, and 500,000 requests at 10 blocks. When the range is 128 blocks or less, the node logs a warning.
The node retries other failures in two layers:
- The transport retries HTTP 429 and 503 responses, and the JSON-RPC errors that mean the same. It
tries up to 8 more times. The first wait is 500 ms, and a
Retry-Aftervalue from the provider replaces it. - The log fetcher sends each range at most 3 times, with waits of 2 s and 4 s between the attempts. After the third failure, the sync stops with an error.
The node does not treat a plan or authorization refusal as a range limit. A narrower range cannot fix it, so the sync fails.
The first sync starts at the lowest deploy_block of all configured contracts. On mainnet, the
published fee_token deploy block (20663730) is about 4.8 million blocks before the
bonding_registry deploy block (25473398). The node does not use fee_token, so you can remove
it from the configuration to shorten the first sync.
Missing log history
An endpoint that dropped old logs can answer a historical eth_getLogs request with an empty result
and no error. The node cannot tell this apart from a range with no events. It finishes the first
sync, reports success, and misses the events that registered it. The node then does not submit
tickets, and the log does not explain why.
This failure is not stable over time. On 16 September 2026, ethereum-sepolia-rpc.publicnode.com
returned 0 logs for the test range of the previous Sepolia deployment. On 25 September 2026, the
same endpoint returned all 67 logs. Test each endpoint yourself, and test it again after a provider
change. Do not rely on the product description of a provider. Free tiers of several well-known
providers do not keep the full history.
Only a log query with a known expected result finds this failure. Do not use a historical state check in its place.
Test an endpoint
Run this test before you use a new endpoint. Replace the URL with the HTTP URL of your endpoint. The
command needs curl and jq.
RPC=https://your-endpoint.example
# Sepolia CiphernodeRegistry, from deployments/manifest.json
CIPHERNODE_REGISTRY_CONTRACT=0x895392D5782E78768A35B1aDfc5b0BD1faB259e4
FROM=$(printf '0x%x' 12050030) # the registry deploy block
TO=$(printf '0x%x' 12050079) # FROM + 49: a range of 50 blocks
# The result must be a log count, not an error and not an empty list.
curl -s -X POST "$RPC" -H 'Content-Type: application/json' \
-d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"eth_getLogs\",\"params\":[{\"fromBlock\":\"$FROM\",\"toBlock\":\"$TO\",\"address\":\"$CIPHERNODE_REGISTRY_CONTRACT\"}]}" \
| jq 'if .error then {error: .error} else {logs: (.result | length)} end'This range holds 7 logs. The range is in the past, so the count does not change.
Read the result this way:
| Result | Meaning |
|---|---|
logs at the expected count | The endpoint keeps this history. |
logs: 0 | The endpoint dropped the history and did not report it. Do not use it. |
An error that names a block range | The range cap is below 10,000 blocks. The node narrows its range, so the endpoint works but syncs slower. |
An error about a plan or a token | The endpoint refuses the request. The node cannot use it. |
To test a larger range, increase TO. The count then changes as the registry emits events, so
compare the result with a second endpoint.
Measured endpoints
These results are for the previous Sepolia deployment, on 25 September 2026. The range was 10,000
blocks from the deploy block of its CiphernodeRegistry, and it held 67 logs. The current
deployment is too new for a range of that size. Provider behavior changes, so run the test yourself
before you trust a row.
| Endpoint | eth_getLogs over the test range | eth_getCode at the deploy block | Usable with the node |
|---|---|---|---|
sepolia.gateway.tenderly.co | 67 logs | 0x (state not served) | Yes |
ethereum-sepolia-rpc.publicnode.com | 67 logs (0 logs on 16 September 2026) | historical state … is not available | Yes on 25 September, but it lost history before |
sepolia.drpc.org | ranges over 10000 blocks are not supported on free plan (also for 3,500 blocks) | chain is not available on free plan | No |
1rpc.io/sepolia | eth_getLogs is limited to 0 - 50 blocks range | state at block … is pruned | Yes, with a slow sync |
sepolia.gateway.tenderly.co returns every log but no historical state. This row shows why a state
check must not replace a log check. 1rpc.io caps the range at 50 blocks. The node narrows its
range below that cap by itself, but a long history then needs many more requests. Use it only as a
fallback.
Paid providers
An archive tier does not prove that an endpoint keeps the log history. Run the test above first. The
first sync of a short history needs a few hundred eth_getLogs requests at the full range. After
the sync, the node keeps one WebSocket subscription and makes occasional reads. The request count is
usually not the deciding factor.
Test the free tier of a paid provider before you use it. Free tiers often cap eth_getLogs to a
much smaller range, and some do not keep old logs. A small range cap only makes the sync slower.
Missing log history makes the endpoint unusable.
Watch a sync
Run the node with -v to see each range that it requests:
Fetched log chunk chain_id=11155111 chunk=1 total_chunks=4 from=11677109 to=11687108 events=125When a provider refuses a range, the node logs a warning and continues with a smaller range:
Provider rejected the block range, narrowing the window and retrying chain_id=11155111
from=11677109 rejected_to=11687108 window=5000 shrinks=1 error=...When the range is 128 blocks or less, the node also logs this warning:
The provider caps eth_getLogs to a narrow range. The first sync needs many requests and is
slow. Use an endpoint with a wider range limit to sync faster.At the end of the sync, the node logs Log fetch complete with the number of chunks and the final
range width.
If the node exits during the first sync
- HTTP 429 or a rate limit message. The node retries these by itself. If it still exits, the limit of the endpoint is too low for a sync. Use another endpoint.
- A restart loop. A first sync that stops before it ends reads the same history again at the
next start. The learned range width is kept in memory only. Startup also has a time limit,
node.startup_timeout_secs(default 1800 s). If the endpoint is too slow to finish in that time, increase the limit or use an endpoint with a wider range. A restart loop against a narrow endpoint does not make progress and uses quota. Under systemd,Restart=alwayswithStartLimitIntervalSec=0keeps the restarts going, so fix the endpoint first. Failed to fetch logs … after 3 retries. If the error text names a block range, the node did not recognize the wording of the provider. Report the exact message. Otherwise, the endpoint refused the request for another reason, for example its plan.- The sync completes, but the node is never selected. Run the log test above. This is the symptom of an endpoint that dropped its history.