getBlockHashAtHeight
The hash of the block at height — the discriminator for detecting a chain RESET (a localnet docker down/up replaces the chain). ChainResetGuard pins a CHECKPOINT block above genesis and re-looks-it-up here on each sync: after a reset the fresh chain is shorter (the pinned height returns BlockHashLookup.NotOnChain) or, once it re-climbs, that height carries a different hash (BlockHashLookup.Found with a new value).
The GENESIS (height-0) hash is deliberately NOT usable for this: a localnet booting from a fixed chain spec reproduces the same genesis hash across a reset (verified live), so a height-0 compare can't tell a reset from a healthy chain. Any block above genesis carries a per-instance timestamp, so its hash IS chain-instance-specific.
GraphQL Query: query { block(offset: { height: $height }) { hash } }
The three-way result keeps the two "can't-read" cases apart, because they mean opposite things:
BlockHashLookup.Found — the block exists; compare its hash.
BlockHashLookup.NotOnChain — a VALID response with no block at that height (the chain is shorter than the checkpoint) → the caller treats this as a reset.
BlockHashLookup.Unavailable — the query itself failed (network / GraphQL / parse) → the caller MUST skip the reset check, so a transient indexer failure can't wipe a healthy wallet.
Defaults to BlockHashLookup.Unavailable so in-memory test doubles needn't implement it (a double that doesn't override it can never trigger a wipe).