Failover moved on a transport failure and never on a JSON-RPC error, both
right — but a node that has pruned the state being asked for does neither. It
answers `{"result": null}`, which is a success, so whichever endpoint happened
to be sticky decided how far back the whole site could see.
The tempting fix is wrong. Null is also the correct answer for most keys read
here: an account with no balance, an item never set, the treasury's
`System::Account` entry, absent because the treasury was never funded. Retrying
every one of those against every endpoint would multiply the load to re-derive
an answer already in hand, and still end in null. The distinction is not in the
response — it is whether the node could have known, which is a property of the
endpoint.
So `classify_depth` probes each one at startup, reading `System::Number` at
block one. That key is derived in a test rather than pasted, because a mistyped
key is absent everywhere and would classify every endpoint as pruned. An
endpoint that answers holds all state after block one; one that cannot be
reached stays `Unknown` and routes as pruned, since an unreachable host must not
become the archive of record by default.
Reads naming a block hash then go through `call_deep`. It tries the sticky
endpoint first, so the common case — mainnet's loopback node, which is an
archive — costs no extra round trip. Only a null from an endpoint never shown to
hold old state buys a second opinion, and `state_call`'s refusal is treated the
same way, because that is how difficulty is read and a node that cannot execute
against dropped state errors rather than returning null.
All seven configured endpoints probe as archives right now, so this changes no
current behaviour. It is what stops history going quietly blank the day one of
them stops being one — and it says so in the log when it happens, because an
endpoint that cannot see as far back as the site is asking is a configuration
fact worth surfacing rather than a chain fact.
Closes#9
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jp6a8EDar9ueEhAxzep4V5