getBlockHashAtHeight

open suspend fun getBlockHashAtHeight(height: Long): BlockHashLookup

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).