Skip to content

fix(dapi): truthful state sync status, health height field collision, client crash on absent sections - #4532

Merged
QuantumExplorer merged 3 commits into
dashpay:v4.2-devfrom
PastaPastaPasta:fix/dapi-status-reporting
Sep 8, 2026
Merged

fix(dapi): truthful state sync status, health height field collision, client crash on absent sections#4532
QuantumExplorer merged 3 commits into
dashpay:v4.2-devfrom
PastaPastaPasta:fix/dapi-status-reporting

Conversation

@PastaPastaPasta

@PastaPastaPasta PastaPastaPasta commented Aug 29, 2026

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

Three status-reporting bugs found during state-sync QA (all observed live on a state-synced local node). Independent of the state-sync feature PRs — these affect today's v4.2-dev.

What was done?

  • rs-dapi: stop emitting misleading all-zero stateSync counters. Tenderdash 1.7 never populates the six state-sync counters in /statusenv.StateSyncMetricer has no assignment site anywhere in tenderdash (the reactor is dropped into node.services with no reference kept, and *statesync.Reactor doesn't even implement the Metricer interface), so the fields are always "0". rs-dapi's emit gate was additionally inverted (json:",string" means the fields are never absent, so the "is any data present" check always passed). getStatus now emits the stateSync section only when one of the six state-sync-specific counters is non-zero — absent rather than "state sync ran and did nothing" — and block-sync progress fields no longer leak into the state-sync section. The precise tenderdash-side fix (wire the reactor into rpcEnv, implement the two missing Metricer methods, snapshot syncer values before syncComplete() nils them, persist across restart) is documented in the code and being addressed separately.
  • rs-dapi: /health no longer reports a Core height under the name latestBlockHeight. getStatus's chain.latestBlockHeight was always correct (tenderdash platform height); the collision was checks.coreRpc.latestBlockHeight on the unversioned /health endpoint, sourced from Core getblockcount — the exact number QA observed. Renamed to coreBlockHeight (no in-repo consumers of the old key), with a regression test pinning that chain.latest_block_height is the platform height.
  • js-dapi-client: getStatus no longer throws on nodes missing optional sections. getStateSync(), getNode(), getChain(), getNetwork(), getTime(), and the nested version getters were all called unguarded; any of them absent (never-synced node, Drive or tenderdash unreachable) threw a TypeError. All optional sub-messages are now guarded, with unit tests for the absent-section shapes.

How Has This Been Tested?

rs-dapi: 307 unit tests passing including 7 new/changed (mid-sync, post-sync, never-synced, block-sync-only fixtures; height-collision regression). js-dapi-client: 320 passing including new absent-section cases. fmt/clippy clean. code-review-validator gate run; both valid findings fixed.

Breaking Changes

/health's checks.coreRpc.latestBlockHeight key is renamed to coreBlockHeight (unversioned diagnostics endpoint; no in-repo consumers — external scrapers of that exact key would need the one-word update). getStatus omits the stateSync section when there is no state-sync data instead of emitting zeros.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation if needed

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Platform status responses now handle missing optional sections without failing.
    • State sync information is shown only when state-sync activity is present, avoiding misleading status data.
    • Platform and Core block heights are now reported from the correct sources.
  • Changes

    • Health endpoint output renames latestBlockHeight to coreBlockHeight to distinguish it from the Platform chain height.

PastaPastaPasta and others added 3 commits August 29, 2026 21:15
…are absent

Every message-typed field of GetStatusResponseV0 is optional on the wire. DAPI omits the state_sync section on nodes that are not state syncing, and omits node/chain/network when Tenderdash is unreachable, so createFromProto threw a TypeError on v0.getStateSync().getTotalSyncedTime() for any ordinary node.

Guard every optional sub-message read: absent node/chain/network/state_sync sections now yield null instead of throwing, and absent version sub-messages yield undefined. Also covers protocol.drive (absent when Drive is down) and protocol.tenderdash, which had the same unguarded chain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
build_state_sync_info gated the state sync section on the Tenderdash strings being non-empty, but Tenderdash serialises every one of those fields as a quoted integer that is always present. The gate passed on every reachable node and getStatus emitted an all-zero StateSync message that asserts "state sync ran and did nothing" - indistinguishable from "this node never state synced", and identical on a node that had just restored a snapshot.

In Tenderdash 1.7 six of the eight values are hardcoded zero regardless: /status only copies the state sync counters when Environment.StateSyncMetricer is set (internal/rpc/core/status.go:91) and nothing ever assigns that field (internal/rpc/core/env.go:86 has no writer). total_synced_time and remaining_time are the only ones that can move, and they belong to the block sync reactor (internal/blocksync/reactor.go:308-328), not state sync. Emit the section only when Tenderdash actually reported a non-zero value, and omit it otherwise so clients can tell the difference.

Also rename the /health Core RPC height from latestBlockHeight to coreBlockHeight. It carries Dash Core's getblockcount, while latestBlockHeight in getStatus's chain section is the Platform height; a freshly joined node showing a Core height of ~8000 next to a Platform height of 28 was read as a broken Platform height. The getStatus mapping itself is correct - Tenderdash's /status exposes no Dash Core chain fields at all.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
total_synced_time and remaining_time are owned by Tenderdash's block sync reactor and are set on any node merely replaying blocks, so treating them as evidence of state sync raised a section named after state sync on nodes that never touched a snapshot. Gate the section on the six genuinely state-sync-specific counters instead.

Also make js-dapi-client's time section follow the same guard as the other optional sections rather than silently yielding undefined field values.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 09842248-8277-474f-ae81-4d8fdb3a3351

📥 Commits

Reviewing files that changed from the base of the PR and between e783c0a and bdff957.

📒 Files selected for processing (4)
  • packages/js-dapi-client/lib/methods/platform/getStatus/GetStatusResponse.js
  • packages/js-dapi-client/test/unit/methods/platform/getStatus/GetStatusResponse.spec.js
  • packages/rs-dapi/src/server/metrics.rs
  • packages/rs-dapi/src/services/platform_service/get_status.rs

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The JavaScript client now tolerates absent status protobuf sections. The Rust status service narrows state-sync detection and distinguishes Platform and Core block heights in health output.

Changes

JavaScript status parsing

Layer / File(s) Summary
Optional status section parsing
packages/js-dapi-client/lib/methods/platform/getStatus/GetStatusResponse.js
The parser guards absent version, node, chain, network, state-sync, and time sections. Nullable constructor and getter types document the resulting values.
Absent section coverage
packages/js-dapi-client/test/unit/methods/platform/getStatus/GetStatusResponse.spec.js
Tests cover missing state-sync data and missing optional status sections.

Rust status reporting

Layer / File(s) Summary
State-sync response detection
packages/rs-dapi/src/services/platform_service/get_status.rs
State-sync output now depends only on state-sync-specific counters. Tests cover empty, block-sync-only, active, completed, and Platform/Core height cases.
Core health height naming
packages/rs-dapi/src/server/metrics.rs
Core RPC health output now uses coreBlockHeight. Serialization tests reject the previous latestBlockHeight field.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Merge Risk: ⚪ Minimal · up to bdff9

Status responses now omit misleading empty state-sync data, distinguish Core height naming, and safely represent absent optional sections without client exceptions. The covered changes are ready to merge.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the three main fixes: truthful state sync status, the health height-field rename, and client handling of absent sections.
Docstring Coverage ✅ Passed Docstring coverage is 92.31% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 13 functions across 4 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@thepastaclaw

thepastaclaw commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

✅ Final review complete — no blockers (commit bdff957)

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Final validation — Sol-only technical fallback

The status builder now omits stateSync unless state-sync-specific counters justify the section, while preserving the existing timing payload when genuine state-sync data is available. The proposed blocker conflates presence gating with payload preservation; the implementation, regression tests, and existing protobuf schema make that distinction explicit, so no actionable findings remain.

Source: reviewer 1: gpt-5.6-sol (agent: sol-fallback-reviewer, role: general); reviewer 2: gpt-5.6-sol (agent: sol-fallback-reviewer, role: rust-quality); final verifier: gpt-5.6-sol (agent: sol-verifier, role: final-verifier)

One or more required Phase-1 GLM Flash lanes remained technically unusable after the bounded exact-model retry. Their evidence was discarded as authoritative, and the complete selected role cohort was rerun fresh on exact gpt-5.6-sol before this fresh Sol verifier produced the final decision. No additional Phase-2 reviewer pass ran.

Review provenance

  • Phase 1 GLM evidence: technically unusable after bounded retry; discarded from the decision
  • GLM failure attempts: codex-general-259753a23f144b6392bd0f78eb193856 (failed), codex-general-55495ae61dd540818d15ac60e0dd5ff6 (failed), codex-rust-quality-478f1aae62394461b64986f1f9b6a58a (failed), codex-rust-quality-8d92ce1cc5b94fdebcf6375637ba6a5f (failed)
  • Sol-only fallback reasons: launch_transport_or_nonzero_exit, launch_transport_or_nonzero_exit
  • Sol-only fallback reviewers: gpt-5.6-sol — general (completed); agent sol-fallback-reviewer, gpt-5.6-sol — rust-quality (completed); agent sol-fallback-reviewer
  • Fresh verifier (Sol): gpt-5.6-sol — final-verifier; agent sol-verifier
  • Additional Phase 2 pass: not run; the Sol-only fallback is final

@PastaPastaPasta
PastaPastaPasta marked this pull request as ready for review September 8, 2026 03:14
@PastaPastaPasta

Copy link
Copy Markdown
Member Author

requesting review @shumkov

@PastaPastaPasta PastaPastaPasta added the ready for final review Ready for the final review. If AI was involved in producing this PR, it has already had a reviewer. label Sep 8, 2026
@QuantumExplorer
QuantumExplorer merged commit e037d42 into dashpay:v4.2-dev Sep 8, 2026
40 checks passed
@PastaPastaPasta
PastaPastaPasta deleted the fix/dapi-status-reporting branch September 8, 2026 15:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready for final review Ready for the final review. If AI was involved in producing this PR, it has already had a reviewer.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants