Proof-backed reads
Quorum verification asks independent providers the same question and compares their answers — its guarantee is "a majority would have to be wrong." Proof mode goes further: the answer is verified cryptographically against the block header, so even the provider that served it could not have faked it. Responses come back with X-RelayGuard-Confidence: proven — a tier above quorum's high.
The trust model
- Header consensus. The gateway fetches the block header (tiny — a few hundred bytes) from your independent provider groups and requires the same quorum agreement as value quorum. Disagreement fails closed (409); too few healthy groups fails closed (503).
- Proof from one provider. The (larger) proof material is fetched from a single provider — any provider, even an untrusted one.
- Verification in the gateway. RelayGuard walks the Merkle-Patricia proof and checks it against the root committed in the agreed header (
stateRootfor account and storage reads,receiptsRootfor transaction receipts). A provider can lie about a value, but it cannot construct a valid proof for a wrong value — the math doesn't allow it.
Honest scope: the value is proven by math; the header it anchors to is agreed by your independent providers. An attacker would need to corrupt your header quorum, not just one data source. Anchoring headers to the beacon chain (removing providers from the trust model entirely) is on the roadmap.
Eligible methods
| Method | Verified how |
|---|---|
eth_getTransactionReceipt | The block's entire receipts trie is rebuilt from the provider's data and its root must equal the agreed header's receiptsRoot — proving the receipt (status, gas, logs and all) is exactly what the chain finalized. |
eth_getStorageAt | Merkle proof for the storage slot against the account's storage root, itself proven against stateRoot. |
eth_getBalance / eth_getTransactionCount | Account proof against stateRoot; balance/nonce read from the proven account leaf. |
eth_getCode | Account proof yields the proven code hash; the returned bytecode must hash to it. |
eth_call (contract view functions) stays on quorum verification today — proving computed results requires re-executing over proven state, which is on the roadmap.
Enabling it
Dashboard → Security Mode → method policy: enable an eligible method and switch its selector from Quorum to Proof. Custom method policy is a Pro feature; changes apply to live traffic within seconds, no client change needed.
Behaviour you should know
- Reads are pinned to finality. A moving block tag (
latest/pending/ omitted) is served at thefinalizedblock, so the proof can never be invalidated by a reorg. Expect data up to ~2 epochs (≈13 min on mainnet) behind the tip — that staleness is the price of an answer that cannot change under you. Explicit block numbers are honored as-is. - Fail closed, retry across providers. A provider whose proof doesn't verify is excluded and the next is tried; if no provider supplies a verifiable proof, you get a 503 — never an unverified answer.
- Graceful degradation to quorum, never to plain proxy. If none of your providers support the required RPC (
eth_getProof/eth_getBlockReceipts), or the case isn't provable (e.g. a transaction that doesn't exist — absence isn't in the trie), the request is verified by quorum instead. The response headers always tell you which guarantee you got. - For receipts, consensus fields are proven; transaction-side fields are not.
status,cumulativeGasUsed,logsBloom, every log's address/topics/data,type, and derivedgasUsed/logIndexcome from verified data.from/to/effectiveGasPricelive in the transaction, not the receipts trie, and are passed through unverified.
The v2 receipt — check us against any node
Proof-verified responses carry a version-2 signed receipt with three extra fields: proofVerified: true, blockHash, and verifiedRoot — the exact trie root the proof was checked against. That makes the verification independently auditable:
# 1. Verify the signature (key from GET /receipts/public-key), decode the receipt.
# 2. Ask ANY node you trust for that block:
curl -X POST https://your-own-node \
-d '{"jsonrpc":"2.0","method":"eth_getBlockByHash",
"params":["<blockHash from receipt>", false],"id":1}'
# 3. The header's receiptsRoot (or stateRoot) must equal the receipt's verifiedRoot.If they match, you've confirmed — without trusting RelayGuard — that the answer your app acted on was verified against the real chain's cryptographic commitment.