Integrity
RPC nodes may return stale or inconsistent data. eRPC's integrity module ensures you always get the most recent and valid blockchain data through:
- Block tracking — Monitors highest known block across all upstreams, ensures
eth_blockNumberandeth_getBlockByNumber(latest/finalized)return the freshest data. - Range enforcement — For
eth_getLogs, ensures the requested block range is available on the chosen upstream. - Response validation — Validates response structure and consistency (bloom filters, receipts, logs). See Validations.
Config
projects:
- id: main
networks:
- architecture: evm
evm:
chainId: 1
# Block tracking: ensure highest known block is returned
integrity:
enforceHighestBlock: true # Replace stale block numbers with highest known
enforceGetLogsBlockRange: true # Skip upstreams that don't have requested range
# State poller settings (how block tracking works)
upstreamDefaults:
evm:
statePollerInterval: 30s # How often to poll latest/finalized block
statePollerDebounce: 5s # Min interval between polls (ideally ≤ block time)Block Tracking
eRPC runs a background state poller for each upstream to track latest/finalized blocks. When enforceHighestBlock: true:
eth_blockNumber: If response is older than highest known, returns highest known instead.eth_getBlockByNumber(latest/finalized): If response is stale, retries on other upstreams.
The poller also updates proactively when eth_blockNumber or eth_getBlockByNumber(latest) returns a higher block than currently tracked.
Metrics: erpc_upstream_stale_latest_block_total, erpc_upstream_stale_finalized_block_total
Range Enforcement for eth_getLogs
When enforceGetLogsBlockRange: true, eRPC checks that the upstream has the requested block range before sending the request:
- If
toBlock> upstream's latest block → skip to next upstream (after forcing a fresh poll if stale) - If
fromBlock< upstream's available range (based onmaxAvailableRecentBlocksconfig) → skip to next upstream
Large range handling: eRPC can auto-split large ranges based on getLogsAutoSplittingRangeThreshold or when upstream returns "range too large" errors.
Metrics: erpc_upstream_evm_get_logs_stale_upper_bound_total, erpc_upstream_evm_get_logs_stale_lower_bound_total, erpc_upstream_evm_get_logs_forced_splits_total
Validations Directives
Response validation directives are ideal for high-integrity use-cases (such as indexing) where you need guaranteed data accuracy. When a validation fails, eRPC treats it as an upstream error — the response is rejected and retry/consensus policies automatically try other upstreams until valid data is found.
How it works with failsafe policies:
Request → Upstream A returns receipts with missing logs (logsBloom doesn't match actual logs)
→ Validation fails → Response rejected (not cached, not returned)
→ Retry policy kicks in → Try Upstream B
→ Upstream B returns complete receipts with matching bloom → Success!With retry: Each validation failure triggers the next retry attempt. Configure maxAttempts high enough to cover your upstream pool.
With consensus: Invalid responses are excluded from consensus voting. Only valid responses participate, so even if 2/3 upstreams return bad data, the 1 valid response wins.
With hedge + consensus + retry (recommended for indexers): Hedge spawns parallel requests, consensus compares valid responses, retry handles cases where all initial attempts fail validation.
Set via config (applies to all requests), HTTP headers, or query parameters:
| Directive | Header | Query | Description |
|---|---|---|---|
validateLogsBloomEmptiness | X-ERPC-Validate-Logs-Bloom-Emptiness | validate-logs-bloom-emptiness | Bloom/logs consistency: logs exist ↔ bloom non-zero |
validateLogsBloomMatch | X-ERPC-Validate-Logs-Bloom-Match | validate-logs-bloom-match | Recalculate bloom from logs and verify match |
enforceLogIndexStrictIncrements | X-ERPC-Enforce-Log-Index-Strict-Increments | enforce-log-index-strict-increments | Log indices must increment by 1 across receipts |
validateTxHashUniqueness | X-ERPC-Validate-Tx-Hash-Uniqueness | validate-tx-hash-uniqueness | No duplicate transaction hashes in receipts |
validateTransactionIndex | X-ERPC-Validate-Transaction-Index | validate-transaction-index | Receipt indices must be sequential (0, 1, 2...) |
validateHeaderFieldLengths | X-ERPC-Validate-Header-Field-Lengths | validate-header-field-lengths | Block header field byte lengths |
validateTransactionFields | X-ERPC-Validate-Transaction-Fields | validate-transaction-fields | Transaction field formats |
validateTransactionBlockInfo | X-ERPC-Validate-Transaction-Block-Info | validate-transaction-block-info | Tx block hash/number matches block |
validateLogFields | X-ERPC-Validate-Log-Fields | validate-log-fields | Log address/topic lengths |
receiptsCountExact | X-ERPC-Receipts-Count-Exact | receipts-count-exact | Receipts array must have exactly N items |
receiptsCountAtLeast | X-ERPC-Receipts-Count-At-Least | receipts-count-at-least | Receipts array must have at least N items |
validationExpectedBlockHash | X-ERPC-Validation-Expected-Block-Hash | validation-expected-block-hash | All receipts must have this block hash |
validationExpectedBlockNumber | X-ERPC-Validation-Expected-Block-Number | validation-expected-block-number | All receipts must have this block number |
projects:
- id: main
networks:
- architecture: evm
evm:
chainId: 1
directiveDefaults:
# Response validations are DISABLED by default (to avoid JSON parsing overhead).
# Enable specific ones as needed for high-integrity use-cases:
validateLogsBloomEmptiness: true
validateLogsBloomMatch: true
enforceLogIndexStrictIncrements: true
# etc.
# Recommended for indexers: hedge + consensus + retry
# Invalid responses are rejected, valid ones are compared, retries if all fail
failsafe:
- matchMethods: "eth_getBlockReceipts"
hedge:
maxCount: 3 # Spawn up to 3 parallel requests
delay: 100ms
consensus:
maxParticipants: 3
agreementThreshold: 2 # Accept if 2+ agree (invalid ones excluded)
retry:
maxAttempts: 5 # Keep trying until valid data found