fix: validate CbTx chainlock height diff#7363
Conversation
|
@coderabbitai review |
✅ Action performedReview finished.
|
✅ No Merge Conflicts DetectedThis PR currently has no conflicts with other open PRs. |
|
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 (4)
🚧 Files skipped from review as they are similar to previous changes (3)
WalkthroughThis PR exports Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes 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 |
|
CI note: the only red item is |
|
✅ Review complete (commit c93049f) |
thepastaclaw
left a comment
There was a problem hiding this comment.
Code Review
Small, well-scoped defensive fix that closes a remote crash vector in CheckCbTxBestChainlock by rejecting out-of-range bestCLHeightDiff before calling GetAncestor(), plus a defensive null check on the returned ancestor. The boundary math is correct and the change is strictly more-rejecting, so it cannot reject any historically valid block. Two minor non-blocking observations about test coverage and reject-reason granularity.
🟡 1 suggestion(s) | 💬 1 nitpick(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/test/evo_cbtx_tests.cpp`:
- [SUGGESTION] src/test/evo_cbtx_tests.cpp:56-62: Defensive GetAncestor() null-check branch is not exercised
Both test cases set bestCLHeightDiff to nHeight (5) and uint32_max, so both trip the range check at specialtxman.cpp:77 and return before the new `if (pAncestor == nullptr)` branch at specialtxman.cpp:89 is reached. The commit message highlights the null check as an intentional defense, but from the unit test's perspective that branch is currently unexercised. A case with `bestCLHeightDiff = nHeight - 1` on a standalone CBlockIndex (pprev == nullptr) would pass the range check, compute curBlockCoinbaseCLHeight = 0, and drive GetAncestor() down a chain it cannot walk, covering the defensive path.
There was a problem hiding this comment.
Code Review
Small, well-scoped consensus-safety fix in CheckCbTxBestChainlock that rejects an out-of-range bestCLHeightDiff before it can produce a pre-genesis ancestor height, plus the defensive null check after GetAncestor() and a focused unit test for the boundary. Current head (dc82f29) addresses prior review nits by renaming the defensive reject reason to bad-cbtx-cldiff-ancestor and documenting why that branch is unreachable from a unit test. No in-scope code defects at this SHA; only a minor commit-history suggestion remains.
💬 1 nitpick(s)
Non-inline nitpick — Squash dc82f29 into 7312fff (or reword its subject)
dc82f29 only touches lines introduced one commit earlier by 7312fff: it renames the defensive reject reason from bad-cbtx-cldiff to bad-cbtx-cldiff-ancestor in src/evo/specialtxman.cpp and adds explanatory comments there and in the new unit test. Functionally this is the final shape of the original fix split across two commits, with the follow-up carrying a test: prefix even though it changes a production consensus-validation reject string in specialtxman.cpp. Since Dash Core preserves PR commits when merging via Merge #NNNN, leaving the split means git bisect/git blame for the reject string lands on a commit whose subject advertises only test changes. Recommend git rebase -i --autosquash (mark dc82f29 as fixup!) to fold it into 7312fff; if the split is intentionally preserved, reword its subject to something like evo: distinguish bad-cbtx-cldiff-ancestor reject reason and describe the rename in the body.
|
CI follow-up on the current red checks:
Leaving the branch unchanged: this does not appear to be an application/test regression in the CbTx height-diff validation change, and I am not going to force-push/no-op rebase just to churn CI. |
dc82f29 to
84ff99f
Compare
|
This pull request has conflicts, please rebase. |
thepastaclaw
left a comment
There was a problem hiding this comment.
Code Review
The CbTx bestCLHeightDiff range check is technically correct and well-tested. Both agents converge on the same single carried-forward nitpick: the follow-up commit 84ff99fd4c is prefixed test: but mutates a production consensus-validation reject string, which becomes misleading permanent history once merged. No new defects in the latest delta.
💬 1 nitpick(s)
| if (pAncestor == nullptr) { | ||
| // Defense-in-depth: the range check above keeps curBlockCoinbaseCLHeight in | ||
| // [0, pindex->nHeight - 1], so GetAncestor() should never return nullptr here. | ||
| return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-cldiff-ancestor"); |
There was a problem hiding this comment.
💬 Nitpick: Follow-up commit titled test: actually renames a production reject string
Carried forward from the prior review at dc82f29959 and still valid at 84ff99fd4c. Commit 84ff99fd4c ("test: clarify CbTx chainlock diff ancestor guard") is not a pure test/comment change: it renames the production consensus-validation reject reason from bad-cbtx-cldiff to bad-cbtx-cldiff-ancestor in src/evo/specialtxman.cpp (the defensive GetAncestor() == nullptr branch). Functionally the result is fine and the differentiated reject reasons are actually clearer than collapsing them — the issue is purely commit hygiene: Dash Core preserves PR commits on merge, so a permanent commit titled test: ... that mutates a consensus-rejection string is misleading to future git log -- src/evo/specialtxman.cpp / git blame / git log -S 'bad-cbtx-cldiff-ancestor' readers. Recommend git rebase -i --autosquash to fold 84ff99fd4c into ebedd1ba5e, or at minimum reword its subject to something like fix: rename CbTx ancestor-guard reject reason so the permanent history accurately describes the production change.
source: ['claude', 'codex']
|
This pull request has conflicts, please rebase. |
Reject an out-of-range bestCLHeightDiff that would otherwise produce a pre-genesis ancestor height when combined with pindex->nHeight, returning state.Invalid(BLOCK_CONSENSUS, "bad-cbtx-cldiff") before any GetAncestor() lookup. The GetAncestor() result is also null-checked as defense-in-depth. CheckCbTxBestChainlock is exposed in specialtxman.h alongside the existing Check* prototypes so a focused unit test can cover the new boundary in src/test/evo_cbtx_tests.cpp. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
84ff99f to
c93049f
Compare
There was a problem hiding this comment.
Code Review
Reviewed current HEAD c93049f311. The CbTx bestCLHeightDiff hardening is clean and well-scoped: the range check bounds the derived chainlock height before cached/ancestor lookup, the defensive GetAncestor() branch is documented, and the unit test covers the rejected boundary values. No blocking issues.
Carried-forward prior findings:
- STILL VALID: the top commit
c93049f311is titledtest: clarify CbTx chainlock diff ancestor guard, but it also renames the production consensus-validation reject reason insrc/evo/specialtxman.cppfrombad-cbtx-cldifftobad-cbtx-cldiff-ancestor. Functionally the rename is fine; the issue is commit-history hygiene because Dash Core preserves PR commits on merge.
New findings in latest delta (84ff99fd..c93049f3): none.
CodeRabbit had 0 inline findings. Coverage note: claude-general and claude-dash-core-commit-history completed; both Codex ACP reviewers failed authentication and produced no findings.
💬 1 nitpick(s)
| if (pAncestor == nullptr) { | ||
| // Defense-in-depth: the range check above keeps curBlockCoinbaseCLHeight in | ||
| // [0, pindex->nHeight - 1], so GetAncestor() should never return nullptr here. | ||
| return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-cldiff-ancestor"); |
There was a problem hiding this comment.
💬 Nitpick: test:-prefixed commit c93049f also renames a production reject string
Verified at HEAD c93049f: the commit titled test: clarify CbTx chainlock diff ancestor guard is not a pure test/comment change. Its diff against src/evo/specialtxman.cpp renames the production consensus reject reason returned at line 155 from bad-cbtx-cldiff to bad-cbtx-cldiff-ancestor (plus a new explanatory comment). Functionally the new name is an improvement — it disambiguates the unreachable defensive branch from the actively-used range-check rejection on line 141, which helps log triage. The issue is purely commit hygiene: dashpay/dash preserves PR commits on merge to develop, so a future git log --grep=cldiff or git blame on this line will land on a commit whose test: subject hides the user-visible reject-string change. Either fold the production rename into the prior fix: commit c892349 and keep only the test/comment additions here, or re-title this commit (e.g. refactor: distinguish CbTx chainlock ancestor reject reason) so the subject reflects the production rename. No code-correctness implication.
source: ['claude']
Issue being fixed or feature implemented
CheckCbTxBestChainlockderived an ancestor height frombestCLHeightDiffwithout first ensuring the derived height was in range. This PR adds an
explicit consensus rejection for out-of-range CbTx chainlock height diffs before
ancestor lookup.
What was done?
bestCLHeightDiffbefore deriving and using the referenced chainlockancestor height.
bad-cbtx-cldifffor out-of-range values.evo_cbtx_testssuite.How Has This Been Tested?
Tested locally on macOS arm64:
Results:
Pre-PR review gate:
Result:
ship.Breaking Changes
None expected. This only rejects malformed CbTx chainlock metadata that
references an invalid ancestor height.
Checklist