Skip to content

fix(blocks): treat a malformed blocks/latest response as a failed poll - #688

Open
2xburnt wants to merge 2 commits into
ping-pub:masterfrom
burnt-labs:pr/block-payload-guard
Open

fix(blocks): treat a malformed blocks/latest response as a failed poll#688
2xburnt wants to merge 2 commits into
ping-pub:masterfrom
burnt-labs:pr/block-payload-guard

Conversation

@2xburnt

@2xburnt 2xburnt commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

fetchLatest trusts the response shape. An endpoint that answers 200 with something other than a block - an error body, a rate-limit page, a proxy interstitial - is stored as latest. Its missing chain_id then fails this check:

if (!this.earliest || this.earliest?...chain_id != this.latest?...chain_id) {
this.earliest = this.latest;
this.recents = [];
}

That branch exists to detect a chain switch, but a junk payload trips it identically, so earliest and recents are wiped. The visible effect is the average block time snapping back to the 1000ms placeholder and the recent-blocks list emptying, intermittently, with nothing surfaced to the user.

Observed against a flaky endpoint: the average block time read 1679 -> 6117 ->
1000 -> 3844 ms against a true 4060 ms, with recents dropping 4 -> 1. With the payload validated it converged to 3957 ms and neither value reset, while a third of responses were still being rejected as malformed.

fetchLatest trusts the response shape. An endpoint that answers 200 with
something other than a block - an error body, a rate-limit page, a proxy
interstitial - is stored as `latest`. Its missing chain_id then fails this
check:

  if (!this.earliest || this.earliest?...chain_id != this.latest?...chain_id) {
    this.earliest = this.latest;
    this.recents = [];
  }

That branch exists to detect a chain switch, but a junk payload trips it
identically, so `earliest` and `recents` are wiped. The visible effect is the
average block time snapping back to the 1000ms placeholder and the recent-blocks
list emptying, intermittently, with nothing surfaced to the user.

Observed against a flaky endpoint: the average block time read 1679 -> 6117 ->
1000 -> 3844 ms against a true 4060 ms, with recents dropping 4 -> 1. With the
payload validated it converged to 3957 ms and neither value reset, while a third
of responses were still being rejected as malformed.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f03fbaced0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/stores/useBaseStore.ts Outdated
The guard only checked `block.header.height`, so a partial response carrying a
height but no chain_id still passed and was assigned to `latest`. The chain_id
comparison immediately below then saw undefined against the previous chain,
reset `earliest` and `recents`, and reproduced exactly the state loss this
change is meant to prevent. Thanks @chatgpt-codex-connector for spotting it -
the original comment even described that mechanism while the condition missed it.

Verified by serving 13 consecutive 200s shaped as
`{ block: { header: { height: "999999" } } }` with chain_id omitted: earliest
stays at 32184677, chain_id stays cosmoshub-4, recents keeps growing, and the
partial is never stored as `latest`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant