fix: invalidate quorum commitment cache on disconnect#7476
Conversation
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
|
✅ Final review complete — no blockers (commit acb611a) |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (2)
WalkthroughThe CbTx quorum commitment hash cache now uses mutex-protected process-lifetime state with whole-result caches and per-LMQ-type LRU entries. A public invalidation helper clears cached results and LRU contents. Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant CQuorumBlockProcessor
participant UndoBlock
participant InvalidateCachedQcHashes
participant CachedGetQcHashesQcIndexedHashes
CQuorumBlockProcessor->>UndoBlock: undo block commitments
UndoBlock->>InvalidateCachedQcHashes: invalidate after non-null commitment undo
InvalidateCachedQcHashes-->>CachedGetQcHashesQcIndexedHashes: clear cached QC hash state
CachedGetQcHashesQcIndexedHashes->>GetMinedCommitment: reload commitment data on cache miss
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
The five container/cache failures in run 29848453760 are the known Each failing job stops at checkout before compiling this branch because the reusable workflow is loaded from base |
thepastaclaw
left a comment
There was a problem hiding this comment.
Final validation — Codex + Sonnet
This PR fixes a real correctness bug where process-lifetime CbTx quorum-commitment hash caches were keyed only by base-block identity, not by the mined CFinalCommitment, allowing a disconnect/reorg to leave stale commitment hashes and incorrectly reject a valid replacement branch with bad-cbtx-quorummerkleroot. I verified the fix directly: InvalidateCachedQcHashes() correctly clears both the outer whole-result cache and the per-quorum LRU under g_qc_hashes_cache_mutex, and is called from CQuorumBlockProcessor::UndoBlock whenever a non-null commitment is undone; the new unit tests demonstrate both the bug (stale hash surviving an evoDb swap) and the fix. The only issue is commit hygiene: the second and third commits are pure iteration on the first commit's own comments/tests within the same PR (one deletes an UndoBlock comment, the next re-adds a shorter version of the same comment one commit later), which is confirmed noise for permanent git blame/bisect history and should be squashed.
Review provenance
- Codex reviewers:
gpt-5.6-sol— general (completed),gpt-5.6-sol— dash-core-commit-history (completed) - Verifier:
claude-sonnet-5— verifier - Sonnet reviewers:
claude-sonnet-5— general (failed),claude-sonnet-5— dash-core-commit-history (failed),claude-sonnet-5— general (completed),claude-sonnet-5— dash-core-commit-history (completed)
🟡 1 suggestion(s)
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `src/llmq/blockprocessor.cpp`:
- [SUGGESTION] src/llmq/blockprocessor.cpp:417-424: Squash the two iterative follow-up commits into the primary fix commit
I diffed all three commits directly. 5a142a8 introduces the cache-invalidation fix, an explanatory comment at the UndoBlock call site, and the regression tests. c39393a then trims that same comment down and simplifies the tests it just added (including removing an EraseMinedCommitment helper and inlining CBlockIndex/genesis setup). 52abbf3 immediately re-adds a shortened version of the identical comment c39393a had just deleted, word-for-word restating the same rationale ("branch-dependent even when the active quorum base-block list is unchanged"). None of these three commits is independently meaningful in permanent history: c39393a's comment removal is undone one commit later, and 52abbf3 exists solely to patch what c39393a just deleted. A future `git blame`/`git log -p` on this line will show a comment added, deleted, and re-added within the same PR — pure noise for bisect/blame with no reviewable content difference. Squash all three into one commit via interactive rebase before merge.
| AddMineableCommitment(qc); | ||
| } | ||
|
|
||
| // Drop both CbTx qc-hash cache layers: mined commitments are branch-dependent | ||
| // even when the active quorum base-block list is unchanged. | ||
| if (undone_commitment) { | ||
| InvalidateCachedQcHashes(); | ||
| } |
There was a problem hiding this comment.
🟡 Suggestion: Squash the two iterative follow-up commits into the primary fix commit
I diffed all three commits directly. 5a142a8 introduces the cache-invalidation fix, an explanatory comment at the UndoBlock call site, and the regression tests. c39393a then trims that same comment down and simplifies the tests it just added (including removing an EraseMinedCommitment helper and inlining CBlockIndex/genesis setup). 52abbf3 immediately re-adds a shortened version of the identical comment c39393a had just deleted, word-for-word restating the same rationale ("branch-dependent even when the active quorum base-block list is unchanged"). None of these three commits is independently meaningful in permanent history: c39393a's comment removal is undone one commit later, and 52abbf3 exists solely to patch what c39393a just deleted. A future git blame/git log -p on this line will show a comment added, deleted, and re-added within the same PR — pure noise for bisect/blame with no reviewable content difference. Squash all three into one commit via interactive rebase before merge.
source: ['claude', 'codex']
There was a problem hiding this comment.
Fixed in 8a897dee04f: squashed the two iterative follow-ups into the primary fix, leaving one signed commit. The resulting tree is byte-identical to the reviewed 52abbf390d3d head; evo_cbtx_tests and git diff --check pass.
52abbf3 to
8a897de
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@src/test/evo_cbtx_tests.cpp`:
- Around line 165-218: Add a local RAII cleanup guard to both
qc_hash_cache_invalidated_on_commitment_branch_change
(src/test/evo_cbtx_tests.cpp lines 165-218) and the UndoBlock test
(src/test/evo_cbtx_tests.cpp lines 232-279) so InvalidateCachedQcHashes() runs
when each test exits, while retaining the existing entry invalidation; ensure
the guard restores the process-global cache even on early failure or exceptions.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 020a5135-daf9-4f95-8d64-2c6c715c31d3
📒 Files selected for processing (4)
src/evo/cbtx.cppsrc/evo/cbtx.hsrc/llmq/blockprocessor.cppsrc/test/evo_cbtx_tests.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
- src/evo/cbtx.cpp
CachedGetQcHashesQcIndexedHashes keyed both process-lifetime layers only by quorum base blocks/hashes, not by the mined CFinalCommitment. Different valid branches can therefore produce different commitment hashes for the same base list. evoDb rolled back on disconnect, but the caches did not, which can make CalcCbTxMerkleRootQuorums reject a valid replacement branch with bad-cbtx-quorummerkleroot. Invalidate both the outer whole-result cache and the per-quorum LRU from CQuorumBlockProcessor::UndoBlock when a non-null commitment is undone, and add unit coverage for the branch-change and UndoBlock invalidation seams. Co-Authored-By: Claude <noreply@anthropic.com>
8a897de to
acb611a
Compare
|
Addressed the latest review and current-head CI findings in amended commit
Local validation: changed translation units syntax-check cleanly, circular-dependency/include-guard/whitespace lints pass, and independent Codex review found no actionable regression. CI has been re-triggered by the push. |
Issue being fixed or feature implemented
CachedGetQcHashesQcIndexedHasheskept process-lifetime caches for CbTx quorum merkle-root calculation:Neither key includes the serialized
CFinalCommitmentthat was actually mined for a base. Different valid branches can mine different valid commitments for the same base list. On disconnect/reorg, evoDb is rolled back correctly, but these caches were not invalidated. That can makeCalcCbTxMerkleRootQuorumsreject an otherwise-valid replacement branch withbad-cbtx-quorummerkleroot.Dash triggerability is lower than a pure PoS setting because of PoW and ChainLocks, but the cache keying is still branch-dependent and therefore a correctness bug.
Out of scope: automatic reconsideration of a chainlocked branch already marked failed.
What was done?
InvalidateCachedQcHashes()to clear both the outer whole-result cache and the per-quorum LRU.CQuorumBlockProcessor::UndoBlockwhen any non-null mined commitment is undone.UndoBlockinvalidation makes the replacement commitment visible.How Has This Been Tested?
src/test/test_dash../src/test/test_dash --run_test=evo_cbtx_tests(pass: 3 cases, including the two new regression cases).Breaking Changes
None.
Checklist: