Skip to content

fix: resolve just-retired quorum by walking earlier masternode lists#801

Open
xdustinface wants to merge 4 commits into
devfrom
fix/quorum-lookup-retired-quorum
Open

fix: resolve just-retired quorum by walking earlier masternode lists#801
xdustinface wants to merge 4 commits into
devfrom
fix/quorum-lookup-retired-quorum

Conversation

@xdustinface
Copy link
Copy Markdown
Collaborator

@xdustinface xdustinface commented Jun 2, 2026

get_quorum_at_height and the FFI ffi_dash_spv_get_quorum_public_key resolved a quorum only through the single nearest masternode list at or below the lookup height. A Platform signing quorum is selected at a lagged height, so by the proof's core_chain_locked_height it can already have retired out of the active set. apply_diff drops a retired quorum from every later list, so the nearest list misses and the lookup fails with Quorum not found, surfacing downstream as InvalidQuorum. The failure is intermittent, firing only at the retirement edge.

The full entry is not actually gone: apply_diff clones the base list without mutating earlier ones, and old per-height lists are retained, so the quorum still lives in every list from its mint height up to retirement.

Add MasternodeListEngine::quorum_entry_for_hash_at_or_before_height, which walks masternode_lists backward from the lookup height and returns the first list still holding the quorum, skipping Invalid entries. Both consumer sites route through it, so the real full QualifiedQuorumEntry is returned with no synthesized data, no new storage, and no API change. The walk is floored at a few active windows below the height (derived from the type's signing_active_quorum_count and DKG interval): a legitimately referenced signing quorum cannot be older than that, so a miss scans a bounded span rather than every accumulated list.

Closes #800.

Summary by CodeRabbit

  • Bug Fixes
    • More reliable quorum lookups across block heights — retired and active quorums are now resolved more consistently, improving lookup accuracy.
    • Simplified, clearer error messages when quorum resolution fails, showing quorum type, requested (locked) height, and quorum identifier for easier troubleshooting.

`get_quorum_at_height` and the FFI `ffi_dash_spv_get_quorum_public_key` resolved a quorum only through the single nearest masternode list at or below the lookup height. A Platform signing quorum is selected at a lagged height, so by the proof's `core_chain_locked_height` it can already have retired out of the active set. `apply_diff` drops a retired quorum from every later list, so the nearest list misses and the lookup fails with `Quorum not found`, surfacing downstream as `InvalidQuorum`. The failure is intermittent, firing only at the retirement edge.

The full entry is not actually gone: `apply_diff` clones the base list without mutating earlier ones, and old per-height lists are retained, so the quorum still lives in every list from its mint height up to retirement.

Add `MasternodeListEngine::quorum_entry_for_hash_at_or_before_height`, which walks `masternode_lists` backward from the lookup height and returns the first list still holding the quorum, skipping `Invalid` entries. Both consumer sites route through it, so the real full `QualifiedQuorumEntry` is returned with no synthesized data, no new storage, and no API change. The walk is floored at a few active windows below the height (derived from the type's `signing_active_quorum_count` and DKG interval): a legitimately referenced signing quorum cannot be older than that, so a miss scans a bounded span rather than every accumulated list.

Closes #800.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 2, 2026

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: Pro

Run ID: 707e5125-19d9-40b0-8dc9-f5ebe4f40e9b

📥 Commits

Reviewing files that changed from the base of the PR and between a241cc3 and b561a1e.

📒 Files selected for processing (1)
  • dash/src/sml/masternode_list_engine/helpers.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • dash/src/sml/masternode_list_engine/helpers.rs

📝 Walkthrough

Walkthrough

Adds MasternodeListEngine::quorum_entry_for_hash_at_or_before_height() with active-window bounded backward search, and updates dash-spv client and the FFI layer to use this API to resolve quorum entries by hash/height.

Changes

Quorum hash-based lookup refactor

Layer / File(s) Summary
Quorum lookup engine and windowing
dash/src/sml/masternode_list_engine/helpers.rs
Adds QUORUM_WALK_BACK_ACTIVE_WINDOWS and quorum_entry_for_hash_at_or_before_height() that computes an active-window floor, iterates retained masternode lists in descending order, skips Invalid entries, and returns (list_height, &QualifiedQuorumEntry). Adds unit tests covering retired/active resolution, missing/out-of-range cases, Invalid skipping, and walk-back floor enforcement.
Dash-spv client queries refactor
dash-spv/src/client/queries.rs
Refactors get_quorum_at_height() to call quorum_entry_for_hash_at_or_before_height() and return the cloned quorum on Some((list_height, quorum)); on miss, logs a warning and returns SpvError::QuorumLookupError with a simplified message.
FFI platform integration refactor
dash-spv-ffi/src/platform_integration.rs
ffi_dash_spv_get_quorum_public_key() now resolves the quorum via quorum_entry_for_hash_at_or_before_height(), writes the 48-byte pubkey to out_pubkey on success, and returns a ValidationError with a focused message (quorum type, locked height, hash) on miss.

Sequence Diagram

sequenceDiagram
  participant FFI
  participant Client
  participant MasternodeListEngine
  participant RetainedMasternodeLists
  FFI->>MasternodeListEngine: quorum_entry_for_hash_at_or_before_height(type, hash, locked_height)
  Client->>MasternodeListEngine: quorum_entry_for_hash_at_or_before_height(type, hash, height)
  MasternodeListEngine->>RetainedMasternodeLists: iterate lists from floor..=height (descending)
  RetainedMasternodeLists-->>MasternodeListEngine: return list (contains quorums map)
  MasternodeListEngine->>MasternodeListEngine: select first matching quorum where status != Invalid
  MasternodeListEngine-->>FFI: Some((list_height, &QualifiedQuorumEntry)) or None
  MasternodeListEngine-->>Client: Some((list_height, &QualifiedQuorumEntry)) or None
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through lists and windows wide,
I chased a hash where quorums hide,
One call now finds what walking missed,
A tidy path, a bounding list,
Happy paws — the pubkey's kissed.

🚥 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 accurately describes the main change: implementing backward walking through masternode lists to resolve just-retired quorums, addressing the core issue.
Linked Issues check ✅ Passed The PR implements the alternative approach to resolve just-retired quorums by walking earlier masternode lists instead of the by-hash fallback proposed in issue #800, achieving the same goal of fixing InvalidQuorum failures at retirement edges.
Out of Scope Changes check ✅ Passed All changes are focused on implementing quorum lookup improvements: adding the new quorum_entry_for_hash_at_or_before_height API, updating both consumer sites to use it, and adding comprehensive tests; no unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/quorum-lookup-retired-quorum

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 and usage tips.

@xdustinface xdustinface marked this pull request as ready for review June 2, 2026 17:13
@xdustinface
Copy link
Copy Markdown
Collaborator Author

@CodeRabbit review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 2, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@dash-spv/src/client/queries.rs`:
- Line 78: The tracing::warn!(message) call treats message as a structured
field; change the tracing::warn! invocation that references the variable message
to use a format string (e.g., tracing::warn!("{}", message)) or the formatter
shorthand (tracing::warn!(%message)) so the text is emitted as the event message
to match the tracing::debug(...) style; update the invocation where
tracing::warn! is called with the local variable message.

In `@dash/src/sml/masternode_list_engine/helpers.rs`:
- Around line 96-101: Remove the redundant QuorumHash import in the test module:
the tests module already brings QuorumHash into scope via use super::*, so
update the use crate::{BlockHash, QuorumHash}; line (inside #[cfg(test)] mod
tests) to only import BlockHash (e.g., use crate::BlockHash;) or otherwise omit
QuorumHash, leaving functions/structs that reference QuorumHash untouched;
reference the test module and the use statement near the top of helpers.rs to
locate and edit the import.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3fa92136-d9bf-4610-a80f-1de727fbf6c4

📥 Commits

Reviewing files that changed from the base of the PR and between a132945 and db34dfc.

📒 Files selected for processing (3)
  • dash-spv-ffi/src/platform_integration.rs
  • dash-spv/src/client/queries.rs
  • dash/src/sml/masternode_list_engine/helpers.rs

Comment thread dash-spv/src/client/queries.rs Outdated
Comment thread dash/src/sml/masternode_list_engine/helpers.rs Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 2, 2026

Codecov Report

❌ Patch coverage is 82.43243% with 26 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.69%. Comparing base (a132945) to head (b561a1e).
⚠️ Report is 1 commits behind head on dev.

Files with missing lines Patch % Lines
dash-spv-ffi/src/platform_integration.rs 0.00% 13 Missing ⚠️
dash-spv/src/client/queries.rs 0.00% 13 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##              dev     #801      +/-   ##
==========================================
+ Coverage   72.67%   72.69%   +0.01%     
==========================================
  Files         322      323       +1     
  Lines       71363    71838     +475     
==========================================
+ Hits        51866    52224     +358     
- Misses      19497    19614     +117     
Flag Coverage Δ
core 76.91% <100.00%> (+0.37%) ⬆️
ffi 45.52% <0.00%> (-0.90%) ⬇️
rpc 20.00% <ø> (ø)
spv 90.19% <0.00%> (-0.11%) ⬇️
wallet 71.29% <ø> (ø)
Files with missing lines Coverage Δ
dash/src/sml/masternode_list_engine/helpers.rs 100.00% <100.00%> (ø)
dash-spv-ffi/src/platform_integration.rs 27.47% <0.00%> (+4.74%) ⬆️
dash-spv/src/client/queries.rs 21.21% <0.00%> (+5.65%) ⬆️

... and 26 files with indirect coverage changes

…rum lookup

Addresses CodeRabbit review comment on PR #801
#801 (comment)
xdustinface added a commit that referenced this pull request Jun 4, 2026
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 4, 2026
The public `quorum_entry_for_hash_at_or_before_height` doc linked to the private `QUORUM_WALK_BACK_ACTIVE_WINDOWS` constant, which rustdoc rejects under `-D rustdoc::private-intra-doc-links`. Demote the link to a plain code span so the Documentation CI job passes.
@github-actions github-actions Bot added the ready-for-review CodeRabbit has approved this PR label Jun 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-review CodeRabbit has approved this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dash-spv: get_quorum_at_height fails for a just-retired Platform quorum (InvalidQuorum) though the pubkey is resident in quorum_statuses

1 participant